[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "## Expected Behavior\n\n\n## Actual Behavior\n\n\n## Steps to Reproduce the Problem\n\n  1.\n  2.\n  3.\n\n## Specifications\n\n  - jGnash Version:\n  - Operating System:\n  - Java Version\n"
  },
  {
    "path": ".github/workflows/ci-linux.yml",
    "content": "name: 'CI Test Linux'\n\non: [push]\n\njobs:\n  test:\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@master\n\n      - uses: actions/cache@master\n        with:\n          path: ~/.gradle/caches\n          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}\n          restore-keys: |\n            ${{ runner.os }}-gradle-\n\n      - uses: actions/setup-java@master\n        with:\n          java-version: '11'\n          architecture: 'x64'\n\n      - run: ./gradlew test\n"
  },
  {
    "path": ".github/workflows/ci-macOS.yml",
    "content": "name: 'CI Test macOS'\n\non: [push]\n\njobs:\n  test:\n\n    runs-on: macOS-latest\n\n    steps:\n      - uses: actions/checkout@master\n\n      - uses: actions/setup-java@master\n        with:\n          java-version: '11'\n          architecture: 'x64'\n\n      - run: ./gradlew test\n"
  },
  {
    "path": ".github/workflows/ci-windows.yml",
    "content": "name: 'CI Test Windows'\n\non: [push]\n\njobs:\n  test:\n\n    runs-on: windows-latest\n\n    steps:\n      - uses: actions/checkout@master\n\n      - uses: actions/setup-java@master\n        with:\n          java-version: '11'\n          architecture: 'x64'\n\n      - run: ./gradlew.bat test\n"
  },
  {
    "path": ".github/workflows/gradle-wrapper-validation.yml",
    "content": "name: \"Validate Gradle Wrapper\"\non: [push, pull_request]\n\njobs:\n  validation:\n    name: \"Validation\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: gradle/wrapper-validation-action@v1\n"
  },
  {
    "path": ".gitignore",
    "content": "**/target/\n**/build/\n**/out/\n**/bin/\n.svn/\n*.directory\n.settings/\n.project\n.classpath\n.idea/\n*.iml\n*.log\nnb-configuration.xml\n*.backup\n/pref.xml\n/gradle-app.setting\n/*.zip\n.gradle/\nlib/\n*.aux\n*.toc\n*.gz\n*.fls\n*.fdb*\n*.out\n\n# keep the exe file for easy dist build on non Windows platfroms\n!rust-launcher/target/release/jGnash.exe"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\n\nsudo: required\n\nbefore_install:\n  - sudo apt-get update -q\n  - sudo apt-get install lib32z1 lib32ncurses5 -y\n\nbefore_script:\n  - if [ \"${TRAVIS_OS_NAME}\" == \"linux\" ]; then export DISPLAY=:99.0 && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16; fi\n\njdk:\n    - openjdk11\n    #- oraclejdk11\n\nbefore_cache:\n  - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock\n  - rm -fr $HOME/.gradle/caches/*/plugin-resolution/\n  - rm -f  $HOME/.gradle/caches/*/fileHashes/fileHashes.bin\n  - rm -f  $HOME/.gradle/caches/*/fileHashes/fileHashes.lock\n\ncache:\n  directories:\n    - $HOME/.gradle/caches/\n    - $HOME/.gradle/wrapper/\n    - $HOME/.m2\n"
  },
  {
    "path": "COPYING",
    "content": " jGnash, a personal finance application\n Copyright (C) 2001-2020 Craig Cavanaugh\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>."
  },
  {
    "path": "README.adoc",
    "content": "image:https://jgnash.github.io/img/jgnash-logo.png[jGnash Logo]\n\n== jGnash README\n\nhttps://sourceforge.net/projects/jgnash/[jGnash] is a free (no strings attached!) personal finance manager with many\nof the same features as commercially-available software. It was created in order to make tracking personal finances\neasy, but also provides the functionality needed by advanced users. jGnash is cross-platform and will run on\nany operating system that has a current Java Runtime Environment (e.g., Linux, Mac OS X, and Microsoft Windows).\n\n* jGnash requires *Java 11* or newer and is compatible with the open source OpenJDK Platform, and the Oracle JVM as well.\n\nSee the <<Requirements>> section below for more details.\n\n=== Contents:\n* <<About, About jGnash>>\n   - <<Features>>\n* <<Donations>>\n* <<Support, Support>>\n* <<Requirements>>\n   - <<Reqs-Java, Java>>\n   - <<Reqs-OS, Supported Operating System versions>>\n* <<Download>>\n* <<Install, Installation>>\n* <<Running, Running jGnash>>\n* <<Development, Building and Development>>\n\n[[About]]\n== About jGnash\n\n[[Features]]\n=== jGnash Features\n\n- Operates on any operating system with Java 11 or newer installed\n- Double Entry Accounting with reconciliation tools\n- OFX, QFX, mt940, and QIF import capabilities\n- Investment Accounts and automatic import of Stocks, Bond, and Funds price history\n- Nestable accounts with automatic rollup of totals and intelligent handling of mixed currencies\n- Reminders with automatic transaction entry\n- Intelligent handling of multiple currencies and exchange rates with automatic online exchange rate updates\n- Printable reports with PDF and spreadsheet export capability\n- XML, Binary, and multiple relational database file formats\n- Supports concurrent multiple users over a network\n\nTo learn more about the features of jGnash, visit the https://sourceforge.net/projects/jgnash/[jGnash Website].\n\nThe jGnash download includes a user manual to help get you started with the basics if you are new to tracking finances.\nIt also covers some of the more subtle features, command line options, and shortcuts that are not immediately obvious.\n\nThe latest version of jGnash uses *OpenJFX* for the user interface. This replaces the old version that used Java Swing\nfor the user interface.  Experienced jGnash users will notice interface improvements.  For example, try using the\nvertical and horizontal scroll wheels in a date picker, and the collapsible transaction forms.\n\n[[Donations]]\n== Donations\n\nDonations are always welcome and appreciated.  This helps to defer the cost of computer hardware and internet access.\n\nhttps://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TYN4QECUL5C44[image:https://img.shields.io/badge/Donate-PayPal-green.svg[PayPal]]\n\n[[Support]]\n== Support\nThe *https://groups.google.com/forum/#!forum/jgnash-user[jGnash Help Group]* is\nalways a good source if you need help and *is the prefered method of contact.*\nYour first post to the group will be moderated to filter spam.\n\nPlease use the search tool to check for similar questions.\n\nThe preferred method of reporting bugs is to use the https://github.com/ccavanaugh/jgnash/issues[Github Issue tracker].\n\n[[Requirements]]\n== Requirements\n\n[[Reqs-Java]]\n=== 1. Java\n\nJava 11 or newer is required to run jGnash.  Unless you have a specific need\nfor a newer version, Java 11 is currently recommended.\n\nUse of a prebuilt installer is recommended.\n\n  - https://www.azul.com/downloads/zulu/[Azul OpenJDK 11] is a branded release that will be easiest to install for most users and is free to use.\n  - https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspot[AdoptOpenJDK] will require manual installation but allows more flexibility and is free to use.\n  - https://jdk.java.net/11/[https://jdk.java.net/11 (OpenJDK)/] will require manual installation and is free to use.\n  - https://www.oracle.com/technetwork/java/javase/downloads/index.html[Oracle Java SE 11] will require manual installation and licensing is required.\n\n[NOTE]\nWhen performing a manual installation of Java, The *JAVA_HOME* Environment\nVariable must be set. Also, the Java bin directory must be added to the execution path.\n\n[NOTE]\nIf you have multiple versions of Java installed on your system, The *JAVA_HOME* Environment\nVariable must be set to Java 11 or newer and the related Java bin directory must be the only version\nin the execution path. Mixing JVM and JDK versions will confuse the bootloader.\n\n*_Use of an OpenJDK package is recommended over use of Oracle JDK due to licensing requirements_*\n\n=== 2. OpenJFX\njGnash uses OpenJFX for the user interface, but will automatically download\nand place the needed components within the lib directly of the jGnash installation.\nPortions of the OpenJFX components are OS specific and cannot be shared between\ndifferent operating systems.\n\n\n[[Reqs-OS]]\n=== 3. Supported Operating Systems: Windows, Linux, or Mac OS X.\n\n==== Microsoft Windows\n\n*  any Windows release that can run the required version of Java\n\n==== Linux\n\n* any Linux distribution that can run the required version of Java\n\n[NOTE]\njGnash is _not compatible_ with GCJ pre-installed on older Linux distributions.\nYou will need to install *OpenJDK 11* for jGnash to operate correctly.\n\n==== Mac OS X\n\n* Mac OS X 10.8.3 or later\n* can run the required version of Java\n\n_Be sure to read <<Install-MacOSX, the section about installing on Mac OS X>> to create the startup script._\n\n\n[[Download]]\n== Download jGnash\n\nYou can download jGnash from the https://sourceforge.net/projects/jgnash/files/Active%20Stable%202.x/[jGnash Download Page].   image:https://img.shields.io/sourceforge/dt/jgnash.svg[\"Download button\", link=\"https://sourceforge.net/projects/jgnash/files/latest/download\"]\n\n[[Install]]\n== To Install jGnash\n\n. Install the latest version of *Java 11*  if you don't already have it installed.\n_jGnash has been tested and is know to work on Java 12 through 14._\n\n** Developers will want the complete Java Development Kit (see build instructions below.)\n. Unzip all files into a directory of your choice leaving the directory structure unchanged.\n\n[[Install-Windows]]\n=== Windows Installation:\n\nSome Windows users with restricted rights may experience write access issues *(Access is denied exception)* with jGnash\ndownloading the JavaFX dependencies.\n\nUnzipping and placing jGnash into `%AppData%\\jGnash` will ensure the users has proper write access.\n\n[[Install-MacOSX]]\n=== Mac OS X Installation:\n\n. Copy the jGnash folder to `/Applications` and remove the version extension so that the final path looks like `/Applications/jGnash`.\n. Create an AppleScript that will run the application:\n.. Open the AppleScript Editor.\n\n.. Create the following script:\n\n\n    try\n        do shell script \"/Applications/jGnash/jGnash\"\n    end try\n\n\n.. Save it as an Application called `jGnash.app` in `/Applications/jGnash`\n\n. Instead of step 2,\n you can set the `/Applications/jGnash/jGnash` file to _Open with..._ `Terminal.app` (the Terminal application).\n\n\n[[Running]]\n== To Run:\n\nExecutable files are provided for Windows and UN*X users at the root of the installation directory. (These are `.exe`\nand `bash shell` files, respectively). Mac OS X users will have created application launch files per\nthe <<Install-MacOSX, Mac installation instructions.>>\n\n[NOTE]\njGnash will need to be restarted after the first launch of a new version.\nOperating System specific files are download and a restart is required for\ncorrect operation.\n\n* Windows: Simply double-click on the jGnash.exe file.\n\n* UN*X / MacOS:  Start jGnash with the provided *jGnash* Bash script.  If jGnash fails to launch, check your file\npermissions and make sure they are set to be executable or use an unzip tool that preserves file permissions.\n\nAn example for UN*X users is shown below assuming you have changed to the installation directory:\n\n[source]\n----\n./jGnash\n----\n\n*Mac OS X:*  Run the application file you created per the <<Install-MacOSX, Mac installation instructions.>>\n\n\n[[Development]]\n== Building and Development\n\nTravis-CI Build Status image:https://travis-ci.org/ccavanaugh/jgnash.svg?branch=master[\"Build Status\", link=\"https://travis-ci.org/ccavanaugh/jgnash\"]\n\n=== Development List\n\nThe https://groups.google.com/forum/#!forum/jgnash-devel[Google Groups jGnash Developer list] is the best\nplace to start if you have questions or ideas.  Initial posts will are moderated to prevent spam.\n\n=== Development Tools\n\nThe IDE used for the development of jGnash is IntelliJ IDEA, but any IDE that supports a Gradle build environment should work.\n\nimage:https://github.com/jGnash/jgnash.github.io/blob/master/img/logo_IntelliJIDEA.png[\"IntelliJIDEA Logo\", height=90, link=\"https://www.jetbrains.com/idea/\"]\n\n\n=== Building jGnash:\n\n*Gradle* is used as the primary build system for jGnash.  The Gradle Wrapper is included (`gradlew` shell and .bat files) so that you do not need to\ninstall Gradle.  The Wrapper will automatically download the necessary dependencies.\n\n[NOTE]\nDepending on your OS (almost always Windows and OSX) the JCE Unlimited Strength Jurisdiction Policy Files for Java\nare needed for the unit tests to complete correctly.  If you do not want to install these files or are\nrestricted by your locale, modify the test build or disable tests.  jGnash uses encryption for client / server\ncommunication and unit tests are performed to prevent regressions.\n\nTo build jGnash you'll need the following software installed and correctly configured on your system:\n\nOpenJDK 11 or later.\n\n_If you are building with a recent 64bit Linux system, you may need to enable Multilib/32 Bit support capabilities.\nOtherwise, the Gradle build may fail when building the windows executables._\n\nTo create the distribution zip file, start at the main directory and run the gradle task to clean and create the distribution:\n\n*Building on Windows:*\n\n[source]\n----\ngradlew clean distZip\n----\n\n*Building on UN*X or Mac OS X:*\n\n[source]\n----\n./gradlew clean distZip\n----\n\n\nThis will run the Gradle tasks necessary to execute core tests and create the distribution file.  The distributable zip\nfile will be produced at the root of the build directory called jGnash-_version_-bin.zip.\n"
  },
  {
    "path": "README.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<meta name=\"generator\" content=\"Asciidoctor 2.0.10\">\n<title>jGnash README</title>\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700\">\n<style>\n/* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */\n/* Uncomment @import statement to use as custom stylesheet */\n/*@import \"https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700\";*/\narticle,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}\naudio,video{display:inline-block}\naudio:not([controls]){display:none;height:0}\nhtml{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}\na{background:none}\na:focus{outline:thin dotted}\na:active,a:hover{outline:0}\nh1{font-size:2em;margin:.67em 0}\nabbr[title]{border-bottom:1px dotted}\nb,strong{font-weight:bold}\ndfn{font-style:italic}\nhr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}\nmark{background:#ff0;color:#000}\ncode,kbd,pre,samp{font-family:monospace;font-size:1em}\npre{white-space:pre-wrap}\nq{quotes:\"\\201C\" \"\\201D\" \"\\2018\" \"\\2019\"}\nsmall{font-size:80%}\nsub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}\nsup{top:-.5em}\nsub{bottom:-.25em}\nimg{border:0}\nsvg:not(:root){overflow:hidden}\nfigure{margin:0}\nfieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}\nlegend{border:0;padding:0}\nbutton,input,select,textarea{font-family:inherit;font-size:100%;margin:0}\nbutton,input{line-height:normal}\nbutton,select{text-transform:none}\nbutton,html input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{-webkit-appearance:button;cursor:pointer}\nbutton[disabled],html input[disabled]{cursor:default}\ninput[type=\"checkbox\"],input[type=\"radio\"]{box-sizing:border-box;padding:0}\nbutton::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}\ntextarea{overflow:auto;vertical-align:top}\ntable{border-collapse:collapse;border-spacing:0}\n*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}\nhtml,body{font-size:100%}\nbody{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:\"Noto Serif\",\"DejaVu Serif\",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}\na:hover{cursor:pointer}\nimg,object,embed{max-width:100%;height:auto}\nobject,embed{height:100%}\nimg{-ms-interpolation-mode:bicubic}\n.left{float:left!important}\n.right{float:right!important}\n.text-left{text-align:left!important}\n.text-right{text-align:right!important}\n.text-center{text-align:center!important}\n.text-justify{text-align:justify!important}\n.hide{display:none}\nimg,object,svg{display:inline-block;vertical-align:middle}\ntextarea{height:auto;min-height:50px}\nselect{width:100%}\n.center{margin-left:auto;margin-right:auto}\n.stretch{width:100%}\n.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}\ndiv,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}\na{color:#2156a5;text-decoration:underline;line-height:inherit}\na:hover,a:focus{color:#1d4b8f}\na img{border:0}\np{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}\np aside{font-size:.875em;line-height:1.35;font-style:italic}\nh1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}\nh1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}\nh1{font-size:2.125em}\nh2{font-size:1.6875em}\nh3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}\nh4,h5{font-size:1.125em}\nh6{font-size:1em}\nhr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}\nem,i{font-style:italic;line-height:inherit}\nstrong,b{font-weight:bold;line-height:inherit}\nsmall{font-size:60%;line-height:inherit}\ncode{font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;font-weight:400;color:rgba(0,0,0,.9)}\nul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}\nul,ol{margin-left:1.5em}\nul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}\nul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}\nul.square{list-style-type:square}\nul.circle{list-style-type:circle}\nul.disc{list-style-type:disc}\nol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}\ndl dt{margin-bottom:.3125em;font-weight:bold}\ndl dd{margin-bottom:1.25em}\nabbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}\nabbr{text-transform:none}\nblockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}\nblockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}\nblockquote cite::before{content:\"\\2014 \\0020\"}\nblockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}\nblockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}\n@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}\nh1{font-size:2.75em}\nh2{font-size:2.3125em}\nh3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}\nh4{font-size:1.4375em}}\ntable{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}\ntable thead,table tfoot{background:#f7f8f7}\ntable thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}\ntable tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}\ntable tr.even,table tr.alt{background:#f8f8f7}\ntable thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}\nh1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}\nh1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}\n.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:\" \";display:table}\n.clearfix::after,.float-group::after{clear:both}\n:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}\n:not(pre)>code.nobreak{word-wrap:normal}\n:not(pre)>code.nowrap{white-space:nowrap}\npre{color:rgba(0,0,0,.9);font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;line-height:1.45;text-rendering:optimizeSpeed}\npre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}\npre>code{display:block}\npre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}\nem em{font-style:normal}\nstrong strong{font-weight:400}\n.keyseq{color:rgba(51,51,51,.8)}\nkbd{font-family:\"Droid Sans Mono\",\"DejaVu Sans Mono\",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}\n.keyseq kbd:first-child{margin-left:0}\n.keyseq kbd:last-child{margin-right:0}\n.menuseq,.menuref{color:#000}\n.menuseq b:not(.caret),.menuref{font-weight:inherit}\n.menuseq{word-spacing:-.02em}\n.menuseq b.caret{font-size:1.25em;line-height:.8}\n.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}\nb.button::before,b.button::after{position:relative;top:-1px;font-weight:400}\nb.button::before{content:\"[\";padding:0 3px 0 2px}\nb.button::after{content:\"]\";padding:0 2px 0 3px}\np a>code:hover{color:rgba(0,0,0,.9)}\n#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}\n#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:\" \";display:table}\n#header::after,#content::after,#footnotes::after,#footer::after{clear:both}\n#content{margin-top:1.25em}\n#content::before{content:none}\n#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}\n#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}\n#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}\n#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}\n#header .details span:first-child{margin-left:-.125em}\n#header .details span.email a{color:rgba(0,0,0,.85)}\n#header .details br{display:none}\n#header .details br+span::before{content:\"\\00a0\\2013\\00a0\"}\n#header .details br+span.author::before{content:\"\\00a0\\22c5\\00a0\";color:rgba(0,0,0,.85)}\n#header .details br+span#revremark::before{content:\"\\00a0|\\00a0\"}\n#header #revnumber{text-transform:capitalize}\n#header #revnumber::after{content:\"\\00a0\"}\n#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}\n#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}\n#toc>ul{margin-left:.125em}\n#toc ul.sectlevel0>li>a{font-style:italic}\n#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}\n#toc ul{font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;list-style-type:none}\n#toc li{line-height:1.3334;margin-top:.3334em}\n#toc a{text-decoration:none}\n#toc a:active{text-decoration:underline}\n#toctitle{color:#7a2518;font-size:1.2em}\n@media screen and (min-width:768px){#toctitle{font-size:1.375em}\nbody.toc2{padding-left:15em;padding-right:0}\n#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}\n#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}\n#toc.toc2>ul{font-size:.9em;margin-bottom:0}\n#toc.toc2 ul ul{margin-left:0;padding-left:1em}\n#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}\nbody.toc2.toc-right{padding-left:0;padding-right:15em}\nbody.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}\n@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}\n#toc.toc2{width:20em}\n#toc.toc2 #toctitle{font-size:1.375em}\n#toc.toc2>ul{font-size:.95em}\n#toc.toc2 ul ul{padding-left:1.25em}\nbody.toc2.toc-right{padding-left:0;padding-right:20em}}\n#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}\n#content #toc>:first-child{margin-top:0}\n#content #toc>:last-child{margin-bottom:0}\n#footer{max-width:100%;background:rgba(0,0,0,.8);padding:1.25em}\n#footer-text{color:rgba(255,255,255,.8);line-height:1.44}\n#content{margin-bottom:.625em}\n.sect1{padding-bottom:.625em}\n@media screen and (min-width:768px){#content{margin-bottom:1.25em}\n.sect1{padding-bottom:1.25em}}\n.sect1:last-child{padding-bottom:0}\n.sect1+.sect1{border-top:1px solid #e7e7e9}\n#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}\n#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:\"\\00A7\";font-size:.85em;display:block;padding-top:.1em}\n#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}\n#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}\n#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}\ndetails,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}\ndetails>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em}\n.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:\"Noto Serif\",\"DejaVu Serif\",serif;font-size:1rem;font-style:italic}\ntable.tableblock.fit-content>caption.title{white-space:nowrap;width:0}\n.paragraph.lead>p,#preamble>.sectionbody>[class=\"paragraph\"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}\ntable.tableblock #preamble>.sectionbody>[class=\"paragraph\"]:first-of-type p{font-size:inherit}\n.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}\n.admonitionblock>table td.icon{text-align:center;width:80px}\n.admonitionblock>table td.icon img{max-width:none}\n.admonitionblock>table td.icon .title{font-weight:bold;font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;text-transform:uppercase}\n.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)}\n.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}\n.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}\n.exampleblock>.content>:first-child{margin-top:0}\n.exampleblock>.content>:last-child{margin-bottom:0}\n.sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px}\n.sidebarblock>:first-child{margin-top:0}\n.sidebarblock>:last-child{margin-bottom:0}\n.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}\n.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}\n.literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;overflow-x:auto;padding:1em;font-size:.8125em}\n@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}\n@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}\n.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=\"highlight\"],.listingblock>.content>pre[class^=\"highlight \"]{background:#f7f7f8}\n.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}\n.listingblock>.content{position:relative}\n.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}\n.listingblock:hover code[data-lang]::before{display:block}\n.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}\n.listingblock.terminal pre .command:not([data-prompt])::before{content:\"$\"}\n.listingblock pre.highlightjs{padding:0}\n.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}\n.listingblock pre.prettyprint{border-width:0}\n.prettyprint{background:#f7f7f8}\npre.prettyprint .linenums{line-height:1.45;margin-left:2em}\npre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}\npre.prettyprint li code[data-lang]::before{opacity:1}\npre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}\ntable.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}\ntable.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}\ntable.linenotable td.code{padding-left:.75em}\ntable.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em}\npre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em}\npre.pygments .lineno::before{content:\"\";margin-right:-.125em}\n.quoteblock{margin:0 1em 1.25em 1.5em;display:table}\n.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}\n.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}\n.quoteblock blockquote{margin:0;padding:0;border:0}\n.quoteblock blockquote::before{content:\"\\201c\";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}\n.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}\n.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}\n.verseblock{margin:0 1em 1.25em}\n.verseblock pre{font-family:\"Open Sans\",\"DejaVu Sans\",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}\n.verseblock pre strong{font-weight:400}\n.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}\n.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}\n.quoteblock .attribution br,.verseblock .attribution br{display:none}\n.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}\n.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}\n.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}\n.quoteblock.abstract{margin:0 1em 1.25em;display:block}\n.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}\n.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}\n.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}\n.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}\n.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0}\ntable.tableblock{max-width:100%;border-collapse:separate}\np.tableblock:last-child{margin-bottom:0}\ntd.tableblock>.content>:last-child{margin-bottom:-1.25em}\ntd.tableblock>.content>:last-child.sidebarblock{margin-bottom:0}\ntable.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}\ntable.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}\ntable.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}\ntable.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}\ntable.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}\ntable.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}\ntable.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}\ntable.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}\ntable.frame-all{border-width:1px}\ntable.frame-sides{border-width:0 1px}\ntable.frame-topbot,table.frame-ends{border-width:1px 0}\ntable.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7}\nth.halign-left,td.halign-left{text-align:left}\nth.halign-right,td.halign-right{text-align:right}\nth.halign-center,td.halign-center{text-align:center}\nth.valign-top,td.valign-top{vertical-align:top}\nth.valign-bottom,td.valign-bottom{vertical-align:bottom}\nth.valign-middle,td.valign-middle{vertical-align:middle}\ntable thead th,table tfoot th{font-weight:bold}\ntbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}\ntbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}\np.tableblock>code:only-child{background:none;padding:0}\np.tableblock{font-size:1em}\nol{margin-left:1.75em}\nul li ol{margin-left:1.5em}\ndl dd{margin-left:1.125em}\ndl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}\nol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}\nul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}\nul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}\nul.unstyled,ol.unstyled{margin-left:0}\nul.checklist{margin-left:.625em}\nul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}\nul.checklist li>p:first-child>input[type=\"checkbox\"]:first-child{margin-right:.25em}\nul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}\nul.inline>li{margin-left:1.25em}\n.unstyled dl dt{font-weight:400;font-style:normal}\nol.arabic{list-style-type:decimal}\nol.decimal{list-style-type:decimal-leading-zero}\nol.loweralpha{list-style-type:lower-alpha}\nol.upperalpha{list-style-type:upper-alpha}\nol.lowerroman{list-style-type:lower-roman}\nol.upperroman{list-style-type:upper-roman}\nol.lowergreek{list-style-type:lower-greek}\n.hdlist>table,.colist>table{border:0;background:none}\n.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}\ntd.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}\ntd.hdlist1{font-weight:bold;padding-bottom:1.25em}\n.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}\n.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}\n.colist td:not([class]):first-child img{max-width:none}\n.colist td:not([class]):last-child{padding:.25em 0}\n.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}\n.imageblock.left{margin:.25em .625em 1.25em 0}\n.imageblock.right{margin:.25em 0 1.25em .625em}\n.imageblock>.title{margin-bottom:0}\n.imageblock.thumb,.imageblock.th{border-width:6px}\n.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}\n.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}\n.image.left{margin-right:.625em}\n.image.right{margin-left:.625em}\na.image{text-decoration:none;display:inline-block}\na.image object{pointer-events:none}\nsup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}\nsup.footnote a,sup.footnoteref a{text-decoration:none}\nsup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}\n#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}\n#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}\n#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}\n#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}\n#footnotes .footnote:last-of-type{margin-bottom:0}\n#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}\n.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}\n.gist .file-data>table td.line-data{width:99%}\ndiv.unbreakable{page-break-inside:avoid}\n.big{font-size:larger}\n.small{font-size:smaller}\n.underline{text-decoration:underline}\n.overline{text-decoration:overline}\n.line-through{text-decoration:line-through}\n.aqua{color:#00bfbf}\n.aqua-background{background:#00fafa}\n.black{color:#000}\n.black-background{background:#000}\n.blue{color:#0000bf}\n.blue-background{background:#0000fa}\n.fuchsia{color:#bf00bf}\n.fuchsia-background{background:#fa00fa}\n.gray{color:#606060}\n.gray-background{background:#7d7d7d}\n.green{color:#006000}\n.green-background{background:#007d00}\n.lime{color:#00bf00}\n.lime-background{background:#00fa00}\n.maroon{color:#600000}\n.maroon-background{background:#7d0000}\n.navy{color:#000060}\n.navy-background{background:#00007d}\n.olive{color:#606000}\n.olive-background{background:#7d7d00}\n.purple{color:#600060}\n.purple-background{background:#7d007d}\n.red{color:#bf0000}\n.red-background{background:#fa0000}\n.silver{color:#909090}\n.silver-background{background:#bcbcbc}\n.teal{color:#006060}\n.teal-background{background:#007d7d}\n.white{color:#bfbfbf}\n.white-background{background:#fafafa}\n.yellow{color:#bfbf00}\n.yellow-background{background:#fafa00}\nspan.icon>.fa{cursor:default}\na span.icon>.fa{cursor:inherit}\n.admonitionblock td.icon [class^=\"fa icon-\"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}\n.admonitionblock td.icon .icon-note::before{content:\"\\f05a\";color:#19407c}\n.admonitionblock td.icon .icon-tip::before{content:\"\\f0eb\";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}\n.admonitionblock td.icon .icon-warning::before{content:\"\\f071\";color:#bf6900}\n.admonitionblock td.icon .icon-caution::before{content:\"\\f06d\";color:#bf3400}\n.admonitionblock td.icon .icon-important::before{content:\"\\f06a\";color:#bf0000}\n.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:\"Open Sans\",\"DejaVu Sans\",sans-serif;font-style:normal;font-weight:bold}\n.conum[data-value] *{color:#fff!important}\n.conum[data-value]+b{display:none}\n.conum[data-value]::after{content:attr(data-value)}\npre .conum[data-value]{position:relative;top:-.125em}\nb.conum *{color:inherit!important}\n.conum:not([data-value]):empty{display:none}\ndt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}\nh1,h2,p,td.content,span.alt{letter-spacing:-.01em}\np strong,td.content strong,div.footnote strong{letter-spacing:-.005em}\np,blockquote,dt,td.content,span.alt{font-size:1.0625rem}\np{margin-bottom:1.25rem}\n.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}\n.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}\n.print-only{display:none!important}\n@page{margin:1.25cm .75cm}\n@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}\nhtml{font-size:80%}\na{color:inherit!important;text-decoration:underline!important}\na.bare,a[href^=\"#\"],a[href^=\"mailto:\"]{text-decoration:none!important}\na[href^=\"http:\"]:not(.bare)::after,a[href^=\"https:\"]:not(.bare)::after{content:\"(\" attr(href) \")\";display:inline-block;font-size:.875em;padding-left:.25em}\nabbr[title]::after{content:\" (\" attr(title) \")\"}\npre,blockquote,tr,img,object,svg{page-break-inside:avoid}\nthead{display:table-header-group}\nsvg{max-width:100%}\np,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}\nh2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}\n#toc,.sidebarblock,.exampleblock>.content{background:none!important}\n#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}\nbody.book #header{text-align:center}\nbody.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}\nbody.book #header .details{border:0!important;display:block;padding:0!important}\nbody.book #header .details span:first-child{margin-left:0!important}\nbody.book #header .details br{display:block}\nbody.book #header .details br+span::before{content:none!important}\nbody.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}\nbody.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}\n.listingblock code[data-lang]::before{display:block}\n#footer{padding:0 .9375em}\n.hide-on-print{display:none!important}\n.print-only{display:block!important}\n.hide-for-print{display:none!important}\n.show-for-print{display:inherit!important}}\n@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}\n.sect1{padding:0!important}\n.sect1+.sect1{border:0}\n#footer{background:none}\n#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}\n@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}\n</style>\n</head>\n<body class=\"article\">\n<div id=\"header\">\n</div>\n<div id=\"content\">\n<div class=\"paragraph\">\n<p><span class=\"image\"><img src=\"https://jgnash.github.io/img/jgnash-logo.png\" alt=\"jGnash Logo\"></span></p>\n</div>\n<div class=\"sect1\">\n<h2 id=\"_jgnash_readme\">jGnash README</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p><a href=\"https://sourceforge.net/projects/jgnash/\">jGnash</a> is a free (no strings attached!) personal finance manager with many\nof the same features as commercially-available software. It was created in order to make tracking personal finances\neasy, but also provides the functionality needed by advanced users. jGnash is cross-platform and will run on\nany operating system that has a current Java Runtime Environment (e.g., Linux, Mac OS X, and Microsoft Windows).</p>\n</div>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>jGnash requires <strong>Java 11</strong> or newer and is compatible with the open source OpenJDK Platform, and the Oracle JVM as well.</p>\n</li>\n</ul>\n</div>\n<div class=\"paragraph\">\n<p>See the <a href=\"#Requirements\">Requirements</a> section below for more details.</p>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_contents\">Contents:</h3>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><a href=\"#About\">About jGnash</a></p>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><a href=\"#Features\">jGnash Features</a></p>\n</li>\n</ul>\n</div>\n</li>\n<li>\n<p><a href=\"#Donations\">Donations</a></p>\n</li>\n<li>\n<p><a href=\"#Support\">Support</a></p>\n</li>\n<li>\n<p><a href=\"#Requirements\">Requirements</a></p>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><a href=\"#Reqs-Java\">Java</a></p>\n</li>\n<li>\n<p><a href=\"#Reqs-OS\">Supported Operating System versions</a></p>\n</li>\n</ul>\n</div>\n</li>\n<li>\n<p><a href=\"#Download\">Download jGnash</a></p>\n</li>\n<li>\n<p><a href=\"#Install\">Installation</a></p>\n</li>\n<li>\n<p><a href=\"#Running\">Running jGnash</a></p>\n</li>\n<li>\n<p><a href=\"#Development\">Building and Development</a></p>\n</li>\n</ul>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"About\">About jGnash</h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"Features\">jGnash Features</h3>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>Operates on any operating system with Java 11 or newer installed</p>\n</li>\n<li>\n<p>Double Entry Accounting with reconciliation tools</p>\n</li>\n<li>\n<p>OFX, QFX, mt940, and QIF import capabilities</p>\n</li>\n<li>\n<p>Investment Accounts and automatic import of Stocks, Bond, and Funds price history</p>\n</li>\n<li>\n<p>Nestable accounts with automatic rollup of totals and intelligent handling of mixed currencies</p>\n</li>\n<li>\n<p>Reminders with automatic transaction entry</p>\n</li>\n<li>\n<p>Intelligent handling of multiple currencies and exchange rates with automatic online exchange rate updates</p>\n</li>\n<li>\n<p>Printable reports with PDF and spreadsheet export capability</p>\n</li>\n<li>\n<p>XML, Binary, and multiple relational database file formats</p>\n</li>\n<li>\n<p>Supports concurrent multiple users over a network</p>\n</li>\n</ul>\n</div>\n<div class=\"paragraph\">\n<p>To learn more about the features of jGnash, visit the <a href=\"https://sourceforge.net/projects/jgnash/\">jGnash Website</a>.</p>\n</div>\n<div class=\"paragraph\">\n<p>The jGnash download includes a user manual to help get you started with the basics if you are new to tracking finances.\nIt also covers some of the more subtle features, command line options, and shortcuts that are not immediately obvious.</p>\n</div>\n<div class=\"paragraph\">\n<p>The latest version of jGnash uses <strong>OpenJFX</strong> for the user interface. This replaces the old version that used Java Swing\nfor the user interface.  Experienced jGnash users will notice interface improvements.  For example, try using the\nvertical and horizontal scroll wheels in a date picker, and the collapsible transaction forms.</p>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Donations\">Donations</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Donations are always welcome and appreciated.  This helps to defer the cost of computer hardware and internet access.</p>\n</div>\n<div class=\"paragraph\">\n<p><a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=TYN4QECUL5C44\"><span class=\"image\"><img src=\"https://img.shields.io/badge/Donate-PayPal-green.svg\" alt=\"PayPal\"></span></a></p>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Support\">Support</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>The <strong><a href=\"https://groups.google.com/forum/#!forum/jgnash-user\">jGnash Help Group</a></strong> is\nalways a good source if you need help and <strong>is the prefered method of contact.</strong>\nYour first post to the group will be moderated to filter spam.</p>\n</div>\n<div class=\"paragraph\">\n<p>Please use the search tool to check for similar questions.</p>\n</div>\n<div class=\"paragraph\">\n<p>The preferred method of reporting bugs is to use the <a href=\"https://github.com/ccavanaugh/jgnash/issues\">Github Issue tracker</a>.</p>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Requirements\">Requirements</h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"Reqs-Java\">1. Java</h3>\n<div class=\"paragraph\">\n<p>Java 11 or newer is required to run jGnash.  Unless you have a specific need\nfor a newer version, Java 11 is currently recommended.</p>\n</div>\n<div class=\"paragraph\">\n<p>Use of a prebuilt installer is recommended.</p>\n</div>\n<div class=\"ulist\">\n<ul>\n<li>\n<p><a href=\"https://www.azul.com/downloads/zulu/\">Azul OpenJDK 11</a> is a branded release that will be easiest to install for most users and is free to use.</p>\n</li>\n<li>\n<p><a href=\"https://adoptopenjdk.net/index.html?variant=openjdk11&amp;jvmVariant=hotspot\">AdoptOpenJDK</a> will require manual installation but allows more flexibility and is free to use.</p>\n</li>\n<li>\n<p><a href=\"https://jdk.java.net/11/\">https://jdk.java.net/11 (OpenJDK)/</a> will require manual installation and is free to use.</p>\n</li>\n<li>\n<p><a href=\"https://www.oracle.com/technetwork/java/javase/downloads/index.html\">Oracle Java SE 11</a> will require manual installation and licensing is required.</p>\n</li>\n</ul>\n</div>\n<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Note</div>\n</td>\n<td class=\"content\">\nWhen performing a manual installation of Java, The <strong>JAVA_HOME</strong> Environment\nVariable must be set. Also, the Java bin directory must be added to the execution path.\n</td>\n</tr>\n</table>\n</div>\n<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Note</div>\n</td>\n<td class=\"content\">\nIf you have multiple versions of Java installed on your system, The <strong>JAVA_HOME</strong> Environment\nVariable must be set to Java 11 or newer and the related Java bin directory must be the only version\nin the execution path. Mixing JVM and JDK versions will confuse the bootloader.\n</td>\n</tr>\n</table>\n</div>\n<div class=\"paragraph\">\n<p><strong><em>Use of an OpenJDK package is recommended over use of Oracle JDK due to licensing requirements</em></strong></p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_2_openjfx\">2. OpenJFX</h3>\n<div class=\"paragraph\">\n<p>jGnash uses OpenJFX for the user interface, but will automatically download\nand place the needed components within the lib directly of the jGnash installation.\nPortions of the OpenJFX components are OS specific and cannot be shared between\ndifferent operating systems.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"Reqs-OS\">3. Supported Operating Systems: Windows, Linux, or Mac OS X.</h3>\n<div class=\"sect3\">\n<h4 id=\"_microsoft_windows\">Microsoft Windows</h4>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>any Windows release that can run the required version of Java</p>\n</li>\n</ul>\n</div>\n</div>\n<div class=\"sect3\">\n<h4 id=\"_linux\">Linux</h4>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>any Linux distribution that can run the required version of Java</p>\n</li>\n</ul>\n</div>\n<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Note</div>\n</td>\n<td class=\"content\">\njGnash is <em>not compatible</em> with GCJ pre-installed on older Linux distributions.\nYou will need to install <strong>OpenJDK 11</strong> for jGnash to operate correctly.\n</td>\n</tr>\n</table>\n</div>\n</div>\n<div class=\"sect3\">\n<h4 id=\"_mac_os_x\">Mac OS X</h4>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>Mac OS X 10.8.3 or later</p>\n</li>\n<li>\n<p>can run the required version of Java</p>\n</li>\n</ul>\n</div>\n<div class=\"paragraph\">\n<p><em>Be sure to read <a href=\"#Install-MacOSX\">the section about installing on Mac OS X</a> to create the startup script.</em></p>\n</div>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Download\">Download jGnash</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>You can download jGnash from the <a href=\"https://sourceforge.net/projects/jgnash/files/Active%20Stable%202.x/\">jGnash Download Page</a>.   <span class=\"image\"><a class=\"image\" href=\"https://sourceforge.net/projects/jgnash/files/latest/download\"><img src=\"https://img.shields.io/sourceforge/dt/jgnash.svg\" alt=\"Download button\"></a></span></p>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Install\">To Install jGnash</h2>\n<div class=\"sectionbody\">\n<div class=\"olist arabic\">\n<ol class=\"arabic\">\n<li>\n<p>Install the latest version of <strong>Java 11</strong>  if you don&#8217;t already have it installed.\n<em>jGnash has been tested and is know to work on Java 12 through 14.</em></p>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>Developers will want the complete Java Development Kit (see build instructions below.)</p>\n</li>\n</ul>\n</div>\n</li>\n<li>\n<p>Unzip all files into a directory of your choice leaving the directory structure unchanged.</p>\n</li>\n</ol>\n</div>\n<div class=\"sect2\">\n<h3 id=\"Install-Windows\">Windows Installation:</h3>\n<div class=\"paragraph\">\n<p>Some Windows users with restricted rights may experience write access issues <strong>(Access is denied exception)</strong> with jGnash\ndownloading the JavaFX dependencies.</p>\n</div>\n<div class=\"paragraph\">\n<p>Unzipping and placing jGnash into <code>%AppData%\\jGnash</code> will ensure the users has proper write access.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"Install-MacOSX\">Mac OS X Installation:</h3>\n<div class=\"olist arabic\">\n<ol class=\"arabic\">\n<li>\n<p>Copy the jGnash folder to <code>/Applications</code> and remove the version extension so that the final path looks like <code>/Applications/jGnash</code>.</p>\n</li>\n<li>\n<p>Create an AppleScript that will run the application:</p>\n<div class=\"olist loweralpha\">\n<ol class=\"loweralpha\" type=\"a\">\n<li>\n<p>Open the AppleScript Editor.</p>\n</li>\n<li>\n<p>Create the following script:</p>\n<div class=\"literalblock\">\n<div class=\"content\">\n<pre>try\n    do shell script \"/Applications/jGnash/jGnash\"\nend try</pre>\n</div>\n</div>\n</li>\n<li>\n<p>Save it as an Application called <code>jGnash.app</code> in <code>/Applications/jGnash</code></p>\n</li>\n</ol>\n</div>\n</li>\n<li>\n<p>Instead of step 2,\nyou can set the <code>/Applications/jGnash/jGnash</code> file to <em>Open with&#8230;&#8203;</em> <code>Terminal.app</code> (the Terminal application).</p>\n</li>\n</ol>\n</div>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Running\">To Run:</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Executable files are provided for Windows and UN*X users at the root of the installation directory. (These are <code>.exe</code>\nand <code>bash shell</code> files, respectively). Mac OS X users will have created application launch files per\nthe <a href=\"#Install-MacOSX\">Mac installation instructions.</a></p>\n</div>\n<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Note</div>\n</td>\n<td class=\"content\">\njGnash will need to be restarted after the first launch of a new version.\nOperating System specific files are download and a restart is required for\ncorrect operation.\n</td>\n</tr>\n</table>\n</div>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>Windows: Simply double-click on the jGnash.exe file.</p>\n</li>\n<li>\n<p>UN*X / MacOS:  Start jGnash with the provided <strong>jGnash</strong> Bash script.  If jGnash fails to launch, check your file\npermissions and make sure they are set to be executable or use an unzip tool that preserves file permissions.</p>\n</li>\n</ul>\n</div>\n<div class=\"paragraph\">\n<p>An example for UN*X users is shown below assuming you have changed to the installation directory:</p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code>./jGnash</code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p><strong>Mac OS X:</strong>  Run the application file you created per the <a href=\"#Install-MacOSX\">Mac installation instructions.</a></p>\n</div>\n</div>\n</div>\n<div class=\"sect1\">\n<h2 id=\"Development\">Building and Development</h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Travis-CI Build Status <span class=\"image\"><a class=\"image\" href=\"https://travis-ci.org/ccavanaugh/jgnash\"><img src=\"https://travis-ci.org/ccavanaugh/jgnash.svg?branch=master\" alt=\"Build Status\"></a></span></p>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_development_list\">Development List</h3>\n<div class=\"paragraph\">\n<p>The <a href=\"https://groups.google.com/forum/#!forum/jgnash-devel\">Google Groups jGnash Developer list</a> is the best\nplace to start if you have questions or ideas.  Initial posts will are moderated to prevent spam.</p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_development_tools\">Development Tools</h3>\n<div class=\"paragraph\">\n<p>The IDE used for the development of jGnash is IntelliJ IDEA, but any IDE that supports a Gradle build environment should work.</p>\n</div>\n<div class=\"paragraph\">\n<p><span class=\"image\"><a class=\"image\" href=\"https://www.jetbrains.com/idea/\"><img src=\"https://github.com/jGnash/jgnash.github.io/blob/master/img/logo_IntelliJIDEA.png\" alt=\"IntelliJIDEA Logo\" height=\"90\"></a></span></p>\n</div>\n</div>\n<div class=\"sect2\">\n<h3 id=\"_building_jgnash\">Building jGnash:</h3>\n<div class=\"paragraph\">\n<p><strong>Gradle</strong> is used as the primary build system for jGnash.  The Gradle Wrapper is included (<code>gradlew</code> shell and .bat files) so that you do not need to\ninstall Gradle.  The Wrapper will automatically download the necessary dependencies.</p>\n</div>\n<div class=\"admonitionblock note\">\n<table>\n<tr>\n<td class=\"icon\">\n<div class=\"title\">Note</div>\n</td>\n<td class=\"content\">\nDepending on your OS (almost always Windows and OSX) the JCE Unlimited Strength Jurisdiction Policy Files for Java\nare needed for the unit tests to complete correctly.  If you do not want to install these files or are\nrestricted by your locale, modify the test build or disable tests.  jGnash uses encryption for client / server\ncommunication and unit tests are performed to prevent regressions.\n</td>\n</tr>\n</table>\n</div>\n<div class=\"paragraph\">\n<p>To build jGnash you&#8217;ll need the following software installed and correctly configured on your system:</p>\n</div>\n<div class=\"paragraph\">\n<p>OpenJDK 11 or later.</p>\n</div>\n<div class=\"paragraph\">\n<p><em>If you are building with a recent 64bit Linux system, you may need to enable Multilib/32 Bit support capabilities.\nOtherwise, the Gradle build may fail when building the windows executables.</em></p>\n</div>\n<div class=\"paragraph\">\n<p>To create the distribution zip file, start at the main directory and run the gradle task to clean and create the distribution:</p>\n</div>\n<div class=\"paragraph\">\n<p><strong>Building on Windows:</strong></p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code>gradlew clean distZip</code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p><strong>Building on UN*X or Mac OS X:</strong></p>\n</div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"CodeRay highlight\"><code>./gradlew clean distZip</code></pre>\n</div>\n</div>\n<div class=\"paragraph\">\n<p>This will run the Gradle tasks necessary to execute core tests and create the distribution file.  The distributable zip\nfile will be produced at the root of the build directory called jGnash-<em>version</em>-bin.zip.</p>\n</div>\n</div>\n</div>\n</div>\n</div>\n<div id=\"footer\">\n<div id=\"footer-text\">\nLast updated 2020-03-24 06:00:42 -0400\n</div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "README.md",
    "content": "<span class=\"image\">![jGnash Logo](https://jgnash.github.io/img/jgnash-logo.png)</span>\n\n## jGnash README\n\n[jGnash](https://sourceforge.net/projects/jgnash/) is a free (no strings attached!) personal finance manager with many of the same features as commercially-available software. It was created in order to make tracking personal finances easy, but also provides the functionality needed by advanced users. jGnash is cross-platform and will run on any operating system that has a current Java Runtime Environment (e.g., Linux, Mac OS X, and Microsoft Windows).\n\n-   jGnash requires **Java 11** or newer and is compatible with the open source OpenJDK Platform, and the Oracle JVM as well.\n\nSee the [Requirements](#Requirements) section below for more details.\n\n### Contents:\n\n-   [About jGnash](#About)\n\n    -   [jGnash Features](#Features)\n\n-   [Donations](#Donations)\n\n-   [Support](#Support)\n\n-   [Requirements](#Requirements)\n\n    -   [Java](#Reqs-Java)\n\n    -   [Supported Operating System versions](#Reqs-OS)\n\n-   [Download jGnash](#Download)\n\n-   [Installation](#Install)\n\n-   [Running jGnash](#Running)\n\n-   [Building and Development](#Development)\n\n## About jGnash\n\n### jGnash Features\n\n-   Operates on any operating system with Java 11 or newer installed\n\n-   Double Entry Accounting with reconciliation tools\n\n-   OFX, QFX, mt940, and QIF import capabilities\n\n-   Investment Accounts and automatic import of Stocks, Bond, and Funds price history\n\n-   Nestable accounts with automatic rollup of totals and intelligent handling of mixed currencies\n\n-   Reminders with automatic transaction entry\n\n-   Intelligent handling of multiple currencies and exchange rates with automatic online exchange rate updates\n\n-   Printable reports with PDF and spreadsheet export capability\n\n-   XML, Binary, and multiple relational database file formats\n\n-   Supports concurrent multiple users over a network\n\nTo learn more about the features of jGnash, visit the [jGnash Website](https://sourceforge.net/projects/jgnash/).\n\nThe jGnash download includes a user manual to help get you started with the basics if you are new to tracking finances. It also covers some of the more subtle features, command line options, and shortcuts that are not immediately obvious.\n\nThe latest version of jGnash uses **OpenJFX** for the user interface. This replaces the old version that used Java Swing for the user interface. Experienced jGnash users will notice interface improvements. For example, try using the vertical and horizontal scroll wheels in a date picker, and the collapsible transaction forms.\n\n## Donations\n\nDonations are always welcome and appreciated. This helps to defer the cost of computer hardware and internet access.\n\n[<span class=\"image\">![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)</span>](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TYN4QECUL5C44)\n\n## Support\n\nThe **[jGnash Help Group](https://groups.google.com/forum/#!forum/jgnash-user)** is always a good source if you need help and **is the prefered method of contact.** Your first post to the group will be moderated to filter spam.\n\nPlease use the search tool to check for similar questions.\n\nThe preferred method of reporting bugs is to use the [Github Issue tracker](https://github.com/ccavanaugh/jgnash/issues).\n\n## Requirements\n\n### 1. Java\n\nJava 11 or newer is required to run jGnash. Unless you have a specific need for a newer version, Java 11 is currently recommended.\n\nUse of a prebuilt installer is recommended.\n\n-   [Azul OpenJDK 11](https://www.azul.com/downloads/zulu/) is a branded release that will be easiest to install for most users and is free to use.\n\n-   [AdoptOpenJDK](https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspot) will require manual installation but allows more flexibility and is free to use.\n\n-   [https://jdk.java.net/11 (OpenJDK)/](https://jdk.java.net/11/) will require manual installation and is free to use.\n\n-   [Oracle Java SE 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html) will require manual installation and licensing is required.\n\n<table>\n<colgroup>\n<col style=\"width: 50%\" />\n<col style=\"width: 50%\" />\n</colgroup>\n<tbody>\n<tr>\n<td class=\"icon\"><div class=\"title\">\nNote\n</div></td>\n<td class=\"content\">When performing a manual installation of Java, The <strong>JAVA_HOME</strong> Environment Variable must be set. Also, the Java bin directory must be added to the execution path.</td>\n</tr>\n</tbody>\n</table>\n\n<table>\n<colgroup>\n<col style=\"width: 50%\" />\n<col style=\"width: 50%\" />\n</colgroup>\n<tbody>\n<tr>\n<td class=\"icon\"><div class=\"title\">\nNote\n</div></td>\n<td class=\"content\">If you have multiple versions of Java installed on your system, The <strong>JAVA_HOME</strong> Environment Variable must be set to Java 11 or newer and the related Java bin directory must be the only version in the execution path. Mixing JVM and JDK versions will confuse the bootloader.</td>\n</tr>\n</tbody>\n</table>\n\n***Use of an OpenJDK package is recommended over use of Oracle JDK due to licensing requirements***\n\n### 2. OpenJFX\n\njGnash uses OpenJFX for the user interface, but will automatically download and place the needed components within the lib directly of the jGnash installation. Portions of the OpenJFX components are OS specific and cannot be shared between different operating systems.\n\n### 3. Supported Operating Systems: Windows, Linux, or Mac OS X.\n\n#### Microsoft Windows\n\n-   any Windows release that can run the required version of Java\n\n#### Linux\n\n-   any Linux distribution that can run the required version of Java\n\n<table>\n<colgroup>\n<col style=\"width: 50%\" />\n<col style=\"width: 50%\" />\n</colgroup>\n<tbody>\n<tr>\n<td class=\"icon\"><div class=\"title\">\nNote\n</div></td>\n<td class=\"content\">jGnash is <em>not compatible</em> with GCJ pre-installed on older Linux distributions. You will need to install <strong>OpenJDK 11</strong> for jGnash to operate correctly.</td>\n</tr>\n</tbody>\n</table>\n\n#### Mac OS X\n\n-   Mac OS X 10.8.3 or later\n\n-   can run the required version of Java\n\n*Be sure to read [the section about installing on Mac OS X](#Install-MacOSX) to create the startup script.*\n\n## Download jGnash\n\nYou can download jGnash from the [jGnash Download Page](https://sourceforge.net/projects/jgnash/files/Active%20Stable%202.x/). <span class=\"image\"><a href=\"https://sourceforge.net/projects/jgnash/files/latest/download\" class=\"image\"><img src=\"https://img.shields.io/sourceforge/dt/jgnash.svg\" alt=\"Download button\" /></a></span>\n\n## To Install jGnash\n\n1.  Install the latest version of **Java 11** if you don’t already have it installed. *jGnash has been tested and is know to work on Java 12 through 14.*\n\n    -   Developers will want the complete Java Development Kit (see build instructions below.)\n\n2.  Unzip all files into a directory of your choice leaving the directory structure unchanged.\n\n### Windows Installation:\n\nSome Windows users with restricted rights may experience write access issues **(Access is denied exception)** with jGnash downloading the JavaFX dependencies.\n\nUnzipping and placing jGnash into `%AppData%\\jGnash` will ensure the users has proper write access.\n\n### Mac OS X Installation:\n\n1.  Copy the jGnash folder to `/Applications` and remove the version extension so that the final path looks like `/Applications/jGnash`.\n\n2.  Create an AppleScript that will run the application:\n\n    1.  Open the AppleScript Editor.\n\n    2.  Create the following script:\n\n            try\n                do shell script \"/Applications/jGnash/jGnash\"\n            end try\n\n    3.  Save it as an Application called `jGnash.app` in `/Applications/jGnash`\n\n3.  Instead of step 2, you can set the `/Applications/jGnash/jGnash` file to *Open with…​* `Terminal.app` (the Terminal application).\n\n## To Run:\n\nExecutable files are provided for Windows and UN\\*X users at the root of the installation directory. (These are `.exe` and `bash shell` files, respectively). Mac OS X users will have created application launch files per the [Mac installation instructions.](#Install-MacOSX)\n\n<table>\n<colgroup>\n<col style=\"width: 50%\" />\n<col style=\"width: 50%\" />\n</colgroup>\n<tbody>\n<tr>\n<td class=\"icon\"><div class=\"title\">\nNote\n</div></td>\n<td class=\"content\">jGnash will need to be restarted after the first launch of a new version. Operating System specific files are download and a restart is required for correct operation.</td>\n</tr>\n</tbody>\n</table>\n\n-   Windows: Simply double-click on the jGnash.exe file.\n\n-   UN\\*X / MacOS: Start jGnash with the provided **jGnash** Bash script. If jGnash fails to launch, check your file permissions and make sure they are set to be executable or use an unzip tool that preserves file permissions.\n\nAn example for UN\\*X users is shown below assuming you have changed to the installation directory:\n\n``` CodeRay\n./jGnash\n```\n\n**Mac OS X:** Run the application file you created per the [Mac installation instructions.](#Install-MacOSX)\n\n## Building and Development\n\nTravis-CI Build Status <span class=\"image\"><a href=\"https://travis-ci.org/ccavanaugh/jgnash\" class=\"image\"><img src=\"https://travis-ci.org/ccavanaugh/jgnash.svg?branch=master\" alt=\"Build Status\" /></a></span>\n\n### Development List\n\nThe [Google Groups jGnash Developer list](https://groups.google.com/forum/#!forum/jgnash-devel) is the best place to start if you have questions or ideas. Initial posts will are moderated to prevent spam.\n\n### Development Tools\n\nThe IDE used for the development of jGnash is IntelliJ IDEA, but any IDE that supports a Gradle build environment should work.\n\n<span class=\"image\"><a href=\"https://www.jetbrains.com/idea/\" class=\"image\"><img src=\"https://github.com/jGnash/jgnash.github.io/blob/master/img/logo_IntelliJIDEA.png\" height=\"90\" alt=\"IntelliJIDEA Logo\" /></a></span>\n\n### Building jGnash:\n\n**Gradle** is used as the primary build system for jGnash. The Gradle Wrapper is included (`gradlew` shell and .bat files) so that you do not need to install Gradle. The Wrapper will automatically download the necessary dependencies.\n\n<table>\n<colgroup>\n<col style=\"width: 50%\" />\n<col style=\"width: 50%\" />\n</colgroup>\n<tbody>\n<tr>\n<td class=\"icon\"><div class=\"title\">\nNote\n</div></td>\n<td class=\"content\">Depending on your OS (almost always Windows and OSX) the JCE Unlimited Strength Jurisdiction Policy Files for Java are needed for the unit tests to complete correctly. If you do not want to install these files or are restricted by your locale, modify the test build or disable tests. jGnash uses encryption for client / server communication and unit tests are performed to prevent regressions.</td>\n</tr>\n</tbody>\n</table>\n\nTo build jGnash you’ll need the following software installed and correctly configured on your system:\n\nOpenJDK 11 or later.\n\n*If you are building with a recent 64bit Linux system, you may need to enable Multilib/32 Bit support capabilities. Otherwise, the Gradle build may fail when building the windows executables.*\n\nTo create the distribution zip file, start at the main directory and run the gradle task to clean and create the distribution:\n\n**Building on Windows:**\n\n``` CodeRay\ngradlew clean distZip\n```\n\n**Building on UN\\*X or Mac OS X:**\n\n``` CodeRay\n./gradlew clean distZip\n```\n\nThis will run the Gradle tasks necessary to execute core tests and create the distribution file. The distributable zip file will be produced at the root of the build directory called jGnash-*version*-bin.zip.\n\nLast updated 2020-03-24 06:00:42 -0400"
  },
  {
    "path": "build.gradle.kts",
    "content": "plugins {\n    id(\"com.github.ben-manes.versions\")\n}\n\nallprojects {\n    repositories {\n        mavenCentral()\n        jcenter()\n        mavenLocal()\n    }\n\n    apply(plugin = \"java\")\n}\n\nsubprojects {\n    group = \"jgnash\"\n    version = \"3.6.0\"\n}"
  },
  {
    "path": "changelog.adoc",
    "content": "== WANTS\n* Support for Derby database\n* CSV Import\n* OFX export\n* VAT/GST UI\n* Command Line interface/Class for loading accounts and transactions\n* Mass selection of destination accounts for imported transactions.\n* Info icon in a sell investment panel that calculate the estimated gains/loss for selling a transaction\n* FIFO, and Lot performance calculations for securities. (http://groups.google.com/group/jgnash-user/browse_thread/thread/fe124ecf806857f3?hl=en)\n* Moving option for a Transaction from one Register to other Register.\n* Hyperlink option from the results in the Reports and Graphs to the concerned Transactions of the Registers.\n* Drill down of split entries in Split Transactions by right clicking on ledger view.\n** Flag budget exceptions?\n* Direct Transaction entry option in the Ledger to avoid Transaction form.\n* Formal Trial Balance report\n* Budgeting\n** annotation, similar to transaction annotation, so the user can reference the reason for an exception in any given period.\n* Reinstate archive capability\n* Open zipped backups instead of manually unzipping.\n\n== Release 3.6.0 __(File format change)__\n* 02/21/2021 Fixed an issue with the JVM returning a negative scale for some locales (GitHub Pull Request #95) _[t-pa]_\n* 02/21/2021 Updated to the latest dependencies.\n* 04/06/2020 Corrected a minor jdbc and Hibernate warning.\n* 03/21/2020 Yahoo financial historical event download format changed.\n* 03/15/2020 Updated to the JavaFX 14 release.\n* 03/07/2020 Waiting for lock files to be removed is now more efficient.\n* 03/01/2020 The numeric format for register balance summaries was not updating with format changes automatically.\n* 03/01/2020 Protect against a divide by zero error for the Tag report.\n* 03/01/2020 Internal cleanup and optimization of application thread management.\n* 02/29/2020 Don't hide the resize columns button when automatic sizing is enabled.\n* 02/29/2020 A race condition would cause Jump actions to fail.\n* 02/29/2020 Zoomed and Jumped register windows were not using the jGnash icon.\n* 02/26/2020 The Tag Manger now filters out application specific tags to make selection less cluttered.\n* 02/25/2020 Expanded Tags to support a much larger icon set.\n* 02/25/2020 Switched icon set from FontAwesome to Material Design.\n* 02/23/2020 Corrected broken Print Icon.\n\n== Release 3.5.1\n* 02/20/2020 Corrected a bug with jGnash.exe causing a failure to launch on Windows Platforms.\n* 02/20/2020 Fixed a bug that was causing icons to no longer work on Windows Platforms.\n\n== Release 3.5.0 __(File format change)__\n* 02/03/2020 Updated to the latest Hibernate, Poi, and Commons CSV dependencies.\n* 02/11/2020 jGnash will now restart automatically for Linux and OSX users after downloading the JavaFX dependencies.\n* 02/10/2020 Restructured main classes to a package to make future development with jpackage cleaner (GitHub Pull Request #88) _[msgilligan]_\n* 02/08/2020 Build system cleanup and consolidated the bootloader.\n* 02/08/2020 Fix RuntimeException when the currency of a locale is unknown. (GitHub Pull Request #92) _[t-pa]_\n* 02/08/2020 Updated to JavaFx 14-ea+8.\n* 02/05/2020 The location for JavaFX dependency downloads has changed.  HTTPS is now used.\n* 02/03/2020 Updated to the latest Picocli, Hikari, PDFBox and Netty dependencies.\n* 01/19/2020 Correctly handle a change to the Yahoo finance cookie expiration date.\n* 01/01/2020 Transactions may now be tagged and filtered with user managed Tags.\n* 12/28/2019 Fixed poor transaction form behavior caused by opening a splits dialog and closing before adding entries.\n* 12/27/2019 Reduced overhead for icon management.\n* 12/26/2019 FontAwesome icons were updated resulting in some icon changes.\n* 12/15/2019 Reworked the Engine API for advanced transaction tags.\n* 12/14/2019 The Windows exe wrapper will now look in the registry if JAVA_HOME has not been set.\n\n== Release 3.4.0\n* 12/14/2019 jGnash now uses a Rust based exe launcher for Windows users that replaces Launch4j.\n* 12/14/2019 Updated to the latest Picocli and Hibernate dependencies.\n* 12/01/2019 Switching the recommended Windows JVM to AdoptOpenJDK.\n* 12/01/2019 Removed old jGnash 2.35.1 workaround for bad UUID values.\n* 12/01/2019 Added a List of Accounts report.\n* 11/30/2019 Added an Export function for the account tree.\n* 11/30/2019 The headers in the CSV export of an account are now localized.\n* 11/30/2019 Changed the internal delimiter for transaction tags to allow use of commas (currently not user accessible).\n* 11/29/2019 Added a Securities chapter to the Manual.\n* 11/29/2019 Prevent a NPE when changing the Symbol of a Security shortly after viewing it's chart.\n* 11/27/2019 Added support for IEXCloud as a data provider for Securities history.\n* 11/16/2019 Switched to a public API introduced in JavaFX 9 to prevent table column reordering in the Budget View.\n* 11/04/2019 Errors and warnings from a failed QIF import were not being displayed.\n* 11/03/2019 Mt940 import now correctly handles files not encoded using ISO_8859_1.\n* 11/03/2019 QIF import now correctly handles files not encoded using UTF8.\n* 10/27/2019 Improved the behavior of the Report dialog when manually changing the scale.  The auto fit size toggles\n             will be cleared automatically.\n* 10/24/2019 Internal cleanup to improve handling of Client/Server network failures.\n* 10/23/2019 Fixed a regression that prevented caching of hash codes.\n* 10/19/2019 Updated to the latest H2 dependency.\n* 10/01/2019 The majority of the build system was migrated to the Kotlin DSL.\n\n== Release 3.3.0\n* 09/21/2019 Updated to the latest Hibernate, HikariCP and PDFBox dependencies.\n* 09/18/2019 The displayed columns for the Portfolio report can now be changed.\n* 09/16/2019 Don't display an exception if tabular report generation is interrupted.\n* 09/15/2019 The Date range of a Portfolio report can now be changed.\n* 09/14/2019 Prevent a rare NPE that would occur when deleting an account shortly after closure of a tabular report.\n* 09/14/2019 Updated to the latest Picocli and Commons Text dependencies.\n* 09/12/2019 Updated to JavaFX 13 dependency.\n* 09/08/2019 Custom style sheets can now be applied to theme the UI.\n* 09/07/2019 Made changes to prevent a very rare ConcurrentModificationException when using a relational database.\n* 09/01/2019 Fixed a source code encoding issue that caused issues when compiling and testing under Windows.\n* 09/01/2019 Updated to the latest Netty dependency.\n* 08/26/2019 The selected file type was ignored when exporting a register and the extension was not specified within\n             the filename.\n* 08/25/2019 Properly restore the configured page size of a report if it's not a custom size.\n* 08/25/2019 Expanded the auto-completion API to improve unit testing robustness.\n* 08/25/2019 Fixed a regression that was causing a failure to load .h2.db files.\n* 08/25/2019 Added a new option to save/restore prior report dates and added a Reset button.  The default start date\n             is now the 1st day of the month.\n* 08/12/2019 Backup files are no longer created unless the data file/database has been changed during a session.\n* 08/11/2019 Consolidated CSS to make customization easier.\n* 08/11/2019 Prevent a transaction form null pointer exception at shutdown when working with a multi-currency files.\n\n== Release 3.2.1\n* 08/10/2019 Updated to JavaFX 13-ea+11 dependency.\n* 08/04/2019 Corrected the column sizing behavior of tabular reports with long values.\n* 08/03/2019 Changed H2 relation databases to use Asynchronous access instead of NIO for safer file access.\n* 08/03/2019 Fixed a bug that was causing File > Save As to force files to a .bxds file if a period existed within the\n             file name's path other than the file extension.\n* 08/03/2019 Internal deduplication and cleanup of relational database code.\n* 08/03/2019 Update to the latest Hibernate dependency.\n* 07/30/2019 Reduced console messages about ignored use of font Layout tables when creating tabular reports.\n* 07/27/2019 Updated to the latest Commons Lang, Commons Text, Commons Collections, Commons CSV, Netty\n             and Picocli dependencies.\n* 07/23/2019 The Create / Modify Security form was not validating the reported Currency had been set.\n* 07/21/2019 Fixed a very old UI bug that would prevent table column width restoration for locales that use a comma\n             for a decimal separator.  It would also trigger unit test failures for the same locales.\n* 07/09/2019 Fixed an issue that was preventing the jGnash.exe file from detecting Java on some systems.\n* 07/09/2019 Improved bootloader behavior when determining \"lib\" location. (GitHub Issue #84) _[Raven Kopelman]_\n* 07/07/2019 Selected tabular rows may now be copied to the clipboard using CTRL-C.\n\n== Release 3.2.0\n* 07/05/2019 Fixed a bug that was preventing entry of investment transaction gains or losses without using the detailed\n             entry form and specifying a gains and loss account.\n* 07/05/2019 Fixed a bug that was causing backup files and database conversions to save in the wrong location or\n             fail completely if a period existed within the file name's path other than the file extension.\n* 07/05/2019 The style of selected odd rows of TreeTables were not consistent with even rows.\n* 06/30/2019 Updated to the latest Netty and PDFBox dependencies.\n* 06/30/2019 The manual was missing from the distribution.\n* 06/30/2019 Converted the manual to Latex and moved to manual process to improve build performance.\n* 06/15/2019 Improved the speed of detecting a non converging IRR calculation.\n* 06/15/2019 Protect against Reminders without descriptions.\n* 06/14/2019 Tabular reports may now be exported to a spreadsheet.\n* 06/01/2019 Updated to the latest Hibernate dependency.\n* 05/26/2019 The current budget period is now highlighted for easier identification.\n* 05/26/2019 Selected table rows were not using the user configured focus color.\n* 05/22/2019 Added a context menu to copy selected transactions to the clipboard.\n\n== Release 3.1.0 __(File format change)__\n* 05/19/2019 Added a Today button to the Budget toolbar for easy refocus of the current period.\n* 05/18/2019 The starting month for Budgets is now configurable.\n* 05/04/2019 Correction for reports with running totals between periods incorrectly hiding accounts when the\n             'hide zero balance accounts' box is selected.\n* 05/03/2019 Improved the size behavior for Alert dialogs on 4K displays (GitHub Issue #82)\n* 05/02/2019 Updated to the latest Netty dependency.\n* 04/28/2019 Cleaned up selection focus visual issues in the Budget view caused by poor JavaFX behavior.\n* 04/28/2019 Protect against unwanted Budget column reordering.\n* 04/28/2019 The account column width may now be changed in the Budget view.\n* 04/28/2019 Updated to JavaFX 12.0.1 dependency\n* 04/27/2019 The rounding mode and scale for Budgets is now configurable.\n* 04/24/2019 A JavaFX exception was being thrown during underlying changes to a budget.\n\n== Release 3.0.4\n* 04/15/2019 The jGnash launch script now works correctly when double clicked in MacOS. (GitHub Pull Request #80) _[Pranay Kumar]_\n* 04/14/2019 Disable the Portfolio report if there are not any investment accounts.\n* 04/14/2019 A NPE could occur if the last investment account was deleted after showing the Portfolio report.\n* 04/13/2019 Eliminated zero width spaces from the export of a register to a xls/xlsx file.\n* 04/13/2019 Updated to latest POI and PDFBox dependencies.\n* 04/07/2019 A Reminders chapter was added to the manual.\n* 04/09/2019 Made the Enabled check box of the New Reminder dialog enabled by default.\n* 04/07/2019 Improved shutdown speed if background events are occurring.\n* 04/05/2019 Made internal changes to prevent race conditions during a shutdown.\n* 04/05/2019 Calculate opening balance if user changes reconcile date. (GitHub Pull Request #79) _[Pranay Kumar]_\n* 04/04/2019 Prevent an NPE caused by a race condition between recurring transactions being processed and an\n             application shutdown in process. (GitHub Issue #78)\n* 04/04/2019 Prevent accumulation of stale internal listeners that would result in wasted system memory and a slowdown\n             during a long running session.\n\n== Release 3.0.3\n* 04/01/2019 Enhanced error handling for relational database to make identification of errors easier.\n* 04/01/2019 Correct validation of numeric input when using a comma as a decimal separator. (GitHub Issue #77)\n\n== Release 3.0.2\n* 03/31/2019 Closing a register window with CTRL-F4 was not working.\n* 03/31/2019 Added a command line option to bypass the bootloader.\n* 03/31/2019 The wrong version information was being reported on the console when requested.\n* 03/30/2019 Automatic column widths will now update correctly if numeric or date formats change.\n* 03/30/2019 Use a full commodity format for the Total column in the investment register.\n* 03/30/2019 Changes to preferred date and numeric formats will now trigger an immediate update of the active register.\n* 03/29/2019 Improved detection and handling of invalid decimal input.\n* 03/29/2019 Removed direct print support from the report dialog.  The user can use save the PDF and print it.\n* 03/28/2019 Reimplemented the page format dialog for reports to address OSX issues and to allow custom paper sizes.\n* 03/28/2019 Prevent an NPE from a race condition between a background security price update and an application shutdown.\n* 03/27/2019 Prevent an NPE from occurring when closing after a report has been shown and GC is slow.\n* 03/26/2019 Improved the update behavior when performing a Save As and when packing databases.\n* 03/26/2019 The pack database action was not listing .mv.db files.\n* 03/24/2019 Cleanup of language files to make translation and updates easier.\n\n== Release 3.0.1\n* 03/22/2019 Corrected a very rare concurrency exception when retrieving investment accounts using a relational database.\n* 03/23/2019 Updated to the latest Hibernate dependency.\n* 03/22/2019 Updated Russian translation. _[pchurzin]_\n* 03/22/2019 Reporting would fail if Java encountered a font file it did not like.\n* 03/21/2019 Fixed wrong currency symbol in Debit/Credit Columns if using a full format. (GitHub Issue #75)\n* 03/19/2019 Do a better job of reporting bootloader network errors.\n* 03/19/2019 Disabled Ctl-C shortcut for closing a file (Conflicts with a paste command).\n* 03/18/2019 Prevent an exception from occurring if a default directory does not exist.\n* 03/16/2019 Changed the download link in the Windows launcher to use a correct JDK.\n\n== Release 3.0.0\n* 03/14/2019 Updated to the latest H2 and Netty dependencies.\n* 03/13/2019 Control of report resolution was added to the Balance Sheet and Net Worth reports.\n* 03/12/2019 Corrected localization issues with the Default Currency and Locale selection dialogs.\n* 03/12/2019 Updated to JavaFX 12 (Java 11 Compatible).\n* 03/10/2019 Made the Options dialog accessible without a file loaded.\n* 03/10/2019 Number formats can now be chosen using the Options dialog and are more consistent.  This allows full\n             control of the display of register values and provides a work around for a known JDK 11 bug.\n* 03/10/2019 Date format selection was moved to the Formats Tab in the Options dialog for UI consistency.\n* 03/08/2019 Disabled generation of the faulty -fx.bat file in the distribution.\n* 03/07/2019 Decimal fields now use an internal math interpreter instead of the Javascript interpreter.\n* 03/05/2019 Updated to latest h2, Apache Poi, and sl4j dependencies.\n* 03/05/2019 Minor internal changes to take advantage of Java 11 APIs.\n\n== Release 3.0.0-b1\n* 02/27/2019 jGnash is designed to operate with Java 11 and newer.\n* 02/27/2019 Removed support for old Swing UI.\n* 02/27/2019 Jasper is no longer used for report generation.  jGnash now uses it's own internal reporting API.\n\n== Release 2.36.2\n* 02/17/2019 Fixed an issue preventing the old Swing UI from running with Java 11 (Swing).\n* 02/10/2019 Prevent an exception when importing odd OFX files using an XML declaration. (GitHub Issue #72)\n* 02/10/2019 Update to the latest Hibernate, Netty, and HikariCP dependencies.  This improves compatibility with Java 9+.\n* 01/14/2019 jGnash would not start on a early access version of Java 8 (Swing, Fx, GitHub Issue #71)\n* 01/11/2019 Corrected an exception when the date picker was cleared and focus was lost (Fx, GitHub PR #70) _[pchurzin]_\n* 12/24/2018 Updated Polish translation (Swing, Fx) _[Sławomir Szarkowicz]_\n* 12/24/2018 Fixed several localization issues reported by Sławomir Szarkowicz.\n* 12/24/2018 Corrected a Runtime exception when trying to create a new file for locales without a country specified (JavaFx, Bug #65) _[valnaumov]_\n\n== Release 2.36.1\n* 11/06/2018 Updated to the latest Commons CSV dependency.\n* 11/05/2018 Potential fix for a ConcurrentModificationException when changing budget properties (Swing, Bug #64)\n* 11/04/2018 Updated to the latest Hibernate, Netty, XStream, and JUnit dependencies.\n* 11/01/2018 Adjust width of the date column to match entry format and font scale. (Fx, GitHub Issue #63)\n* 10/07/2018 Improved handling of OFXv2 files with incorrectly escaped XML characters. (Swing, Fx, GitHub Issue #61)\n* 10/01/2018 Currency exchange rate is working again.  Yahoo continues to lock down their API. (Swing, Fx) _[Pranay Kumar]_\n* 10/01/2018 Updated German translation. (Swing, Fx) _[Alex Werz]_\n* 09/30/2018 Fixed an NPE when an ISIN was not specified for a security. (Swing, Fx) _[Pranay Kumar]_\n* 09/16/2018 The new file wizard would not behave correctly if the task list was used instead of stepping sequentially\n             using the Next button. This also impacted the Import Wizard. (Fx)\n\n== Release 2.36.0\n* 09/13/2018 Enhanced the MT940 parser to allow for an optional currency designator in decimal values. (Swing, Fx) _[Alex Werz]_\n* 09/13/2018 Reinstated check and correct for data files with multiple root accounts and config objects. (Swing, Fx)\n* 09/13/2018 The Fx interface now uses picocli for command line processing. (Fx)\n* 09/13/2018 The old Swing interface no longer supports command line processing. (Swing)\n* 09/10/2018 Fixed a bug that was preventing initialization of a new user specified portable preference file. (Fx)\n* 09/09/2018 Fixed a random stability issue with client / server operation discovered during unit testing. (Swing, Fx)\n* 09/09/2018 Updated to the latest Apache POI dependency.\n* 09/09/2018 Updated manual with proper use of escape characters on the command line for file names.\n* 09/06/2018 Dropped use of log4j as it is no longer a needed dependency.\n\n== Release 2.35.1\n* 08/26/2018 Updated to the latest Netty dependency.\n* 08/25/2018 Fixed a bug when loading files using a very old UUID format. (Swing, Fx)\n* 08/24/2018 Fixed several large memory leaks in the jGnashFx user interface. (Fx)\n* 08/18/2018 Tightened up API for adding and removing securities to accounts to prevent corruption.\n* 08/17/2018 Updated to the latest Hibernate dependency.\n\n== Release 2.35.0 __(File format change)__\n=== Notes:\nRelational databases will need to be saved to a .xml or .bxds file format in the prior release of jGnash.  They may\nbe saved back to a relational database format afterwards.\n\n* 08/12/2018 Fixed a layout bug that was preventing the Investment Transaction dialog from showing the full form.  (Fx)\n* 08/12/2018 Improved the layout behavior of the Transaction dialog.  (Fx)\n* 08/12/2018 Fixed a bug that was causing decimal artifacts to occur in empty rows of the reminders table when using\n             Java 10. (Fx)\n* 08/12/2018 Prevent an \"illegal reflective access operation\" from occurring on Java 9 and newer.\n* 08/10/2018 Reimplemented the detailed gains and loss control to support use in Java 10. (Fx)\n* 07/29/2018 Changes were made to make migration to Java 10+ easier.\n* 07/29/2018 Use Stax instead of kxml to make migration to Java 10+ easier.\n* 07/28/2018 Migrated test system to JUnit 5.\n* 07/28/2018 Updated to the latest Netty dependency.\n* 07/06/2018 Reduced memory usage and improved performance for relational database users.\n* 07/01/2018 Removed support for handling old XML file formats from 2017 and detection of 1.x files.\n* 07/01/2018 Removed Dump Heap button from Console Dialog because API use is restricted in Java 9 and newer. (Swing)\n* 06/24/2018 Replaced c3p0 with HikariCP for reduced application size and improved performance.\n* 06/24/2018 Updated to the latest Hibernate dependency.  This breaks schema compatibility with older relational\n             database files.\n* 06/24/2018 Dropped support for the old jgnash Hibernate persistence unit / schema.\n\n== Release 2.34.1\n* 06/07/18 Updated Russian translation. (Swing, Fx) _[pchurzin]_\n* 06/07/18 Updated to the latest Hibernate, Netty, Hsqldb, and DynamicJasper dependencies.\n* 06/06/18 Remove stale relational database lock files if a crash had occurred.\n* 03/28/18 Updated to the latest H2 dependency.\n* 02/06/18 Improved snooze behavior for reminders. (Fx) _[leeboardtools]_\n* 02/05/18 Corrected a race condition in the transaction register that would cause a rare sorting issue and IndexOutOfBoundsExceptions. (Fx)\n* 02/03/18 Updated to the latest Netty dependency.\n* 02/02/18 Corrected an IllegalStateException when manually reconciling transactions. (Fx)\n* 01/12/18 Nested Investment accounts were summing with small fractional errors depending on Market price. (Swing, Fx)\n\n== Release 2.34.0\n* 01/06/18 Significant update to the Polish translation _[Sławomir Szarkowicz]_\n* 01/06/18 Updated to latest Netty dependency.\n* 12/10/17 Another significant update for the zh-ch locale translation. (Swing, Fx) _[kevinzhwl]_\n* 12/02/17 The Portfolio report now calculates Internal Rate of Return. (Swing, Fx) _[t-pa]_\n* 11/28/17 Improve MT940 import to handle Kontobezeichung (Swing, Fx) _[laeubi and sschuberth]_\n* 11/22/17 Prevent a deadlock due to a poor or corrupt printer configuration at the OS level. (Swing, Fx)\n* 11/22/17 Fixed a Platform thread exception on exit when the application was closed soon after closing a Reminder dialog. (Fx)\n* 11/19/17 Expanded the import filter script interface to allow advanced manipulation of ImportTransactions. (Fx)\n* 11/18/17 Switched from Opencsv to Apache Commons CSV for exports to reduce distribution size and dependencies. (Swing, Fx)\n\n== Release 2.33.2\n* 11/12/17 The opening Reconcile dialog now has a button to calculate ending balance based on the closing date. (Fx) _[Pranay Kumar]_\n* 11/11/17 The Reminders dialog would not close properly if dismissed with a button. (Fx)\n* 11/11/17 The Reminders dialog was not correctly restoring the last used snooze period. (Fx)\n* 11/11/17 Corrected a Hibernate configure error for Account objects that may have been causing subtle bugs. (Swing, Fx)\n* 11/08/17 New workaround for Yahoo discontinuing a portion of their Securites history API. (Swing, Fx)\n* 11/06/17 Correct handling of special characters when importing OFX files. (Swing, Fx, GitHub Issue #35)\n* 11/06/17 Ignore cash transfers for dividends in realized gains calculations. (Swing, Fx) _[t-pa]_\n* 11/06/17 Significant update for the zh-ch locale translation. (Swing, Fx) _[kevinzhwl]_\n* 10/22/17 Updated to latest Hibernate and Netty dependencies.\n* 08/19/17 Switched from a MD5 to SHA-256 hash function for encrypted client / server operation. (Swing, Fx)\n* 08/19/17 Protect against the import of an OFX file with malicious content. (Swing, Fx)\n\n== Release 2.33.1\n* 08/18/17 Corrected an issue with optimal Date storage in xml and bxds files caused by a regression introduced in 2.33.0\n* 08/16/17 The OFX export will now generate required information for transfer between accounts. (Swing, Fx)\n* 08/15/17 Simple use of portable preferences was failing with use of -p or --portable. (Swing, Fx)\n* 08/15/17 Display a console message with the successful uninstallation of application preferences. (Swing, Fx)\n* 08/15/17 The command line help system was not being displayed on the console correctly. (Swing, Fx)\n* 08/14/17 OFX import now supports transfers between accounts. (Fx)\n* 08/13/17 The open dialog was incorrectly allowing selection of a file when a remote connection was selected. (Fx)\n* 08/13/17 Internal were changes made to allow operation with Java 9 with the addition of the command line option\n           `--add-modules java.xml.bind`. (Swing, Fx)\n\n== Release 2.33.0\n* 08/11/17 Updated to the latest Netty dependency.\n* 08/09/17 Implemented amortized payments for the Fx interface. (Fx)\n* 07/31/17 Build system has been converted to Gradle.  Unix executable shell scripts are now provided.\n* 07/31/17 Updated to the latest jopt-simple dependency.\n* 07/09/17 Updated to the latest DynamicJasper and JasperReports dependencies.\n* 07/09/17 Updated to the latest Apache POI dependency.\n* 07/02/17 Further improvements to handling Yahoo Fiance API errors.\n* 07/02/17 Potential fix for a Budget Exception occurring when OSX users are using a relational database.\n* 06/19/17 Added a command button to execute a Reminder on demand. (Fx)\n* 06/16/17 Updated to the latest Netty dependency.\n* 06/16/17 Updated to the latest XStream dependency.\n* 06/16/17 Updated to the latest H2 database dependency.\n\n== Release 2.32.0\n* 06/13/17 Updated to the new Yahoo Finance API for retrieving historical stock price information.\n* 06/12/17 The security history chart would incorrectly show a prior chart if no data existed. (Fx)\n* 06/11/17 Updated to the new Yahoo Finance API for retrieving dividend and stock split information.\n* 06/03/17 Expanded the manual content for importing transactions.\n* 06/03/17 Fixed a regression that was preventing the selection of the transaction's account when importing. (Fx)\n* 05/30/17 Added the ability to pre-process imported transaction memos and payees using user supplied JavaScript. (Fx)\n* 05/28/17 Minor improvements to the button behavior when editing the transaction number list. (Fx)\n* 05/22/17 Updated to the latest Netty dependency.\n* 05/14/17 Minor internal changes to remove the dependency on ControlsFX. (Fx)\n* 05/14/17 The Enter button should be disabled if the form is not valid for investment transactions and split entries. (Fx)\n* 05/13/17 Reworked exchange rate popup because display quality was inconsistent when first shown. (Fx)\n* 05/11/17 Fixed missing icons for the currency exchange rate dialog. (Fx)\n\n== Release 2.31.0\n* 05/10/17 Added a General configuration option to allow full manual control of table column widths. (Fx)\n* 05/10/17 The Options dialog now remembers the last tab that was used. (Fx)\n* 05/08/17 Corrected handling of OFX files written with a windows-1252 character set. (Swing, Fx)\n* 05/08/17 Prevent ghosting horizontal scrollbars when resizing the main window. (Fx)\n* 05/07/17 Table Column sizes (register & reconcile) are now correctly remembered, restored, and scaled. (Fx)\n* 05/06/17 Updated to the latest Netty dependency.\n* 05/06/17 The reminder dialog now closes itself automatically if it was shown in the background while a file close was\n           started concurrently. (Fx)\n* 04/28/17 Dependency on FontAwesomeFx is no longer needed. (Fx)\n* 04/24/17 Updated to the latest H2 database dependency.\n* 04/17/17 Yahoo Security Download now requires use of HTTPS for downloads. (Swing, Fx)\n* 04/17/17 Improved sizing of the open dialog for the Fx interface (Fx, GitHub Issue #25) _[Pranay Kumar]_\n* 04/17/17 Cleaned up build system.  JGoodies dependencies now come from Maven Central\n* 04/15/17 Updated to the latest Hibernate and HSQLDB dependencies.\n* 04/10/17 Corrected an IndexOutOfBoundsException occurring during Transaction import (OFX, QIF) of a quantity not large\n           enough to fill the table. (Fx)\n* 04/09/17 Entry of date separators is now more flexible and allows use of ',' '.' '/' and '\\' characters for all locales. (Fx)\n* 04/09/17 Relaxed date entry requirements.  Single digit months may be now be typed in. (Swing, Fx)\n* 04/09/17 The Account Register report was not reporting split entries correctly and consistent with the UI. (Fx)\n\n== Release 2.30.0\n* 04/09/17 Fixed a bug that was causing Buy and Sell transactions not using the cash balance of the investment account\n           to generate an incorrect cash account amount. (Fx)\n* 04/06/17 Fixed an issue with importing OFX 1.x files with ugly white space formatting. (Swing, Fx)\n* 03/30/17 Added support for the H2 MVStore database file format.\n* 03/30/17 Updated to the latest H2 database dependency\n* 03/26/17 Updated to the latest Hibernate dependency.\n* 03/25/17 The payee for Reinvested Dividends was not being generated correctly. (Swing, Fx)\n* 03/24/17 OFX import of investment transactions is supported for Buys, Sells, Dividends, and Reinvested Dividends.\n* 03/22/17 Corrected a random IllegalStateException occurring during transaction edits. (Fx)\n* 03/11/17 Updated to the latest Netty dependency.\n\n== Release 2.29.0\n* 02/25/17 Improved UI performance when performing large batch updates of transactions. (Fx, GitHub Issue #23)\n* 02/24/17 Updated to the latest Hibernate dependency.\n* 02/22/17 Backup files were not being preserved correctly in some instances depending on the pattern of the file names\n           in the same directory and if they contained a '-' character. (Swing, Fx)\n* 02/13/17 jGnashFx Users are required to use Java 8 Update 71 or newer due to critical Java bugs. (Fx)\n* 02/11/17 Clicking on an Income or Expense bar within the Income Expense Bar Chart will show the details for the\n           period within a pie chart. (Fx) _[Pranay Kumar]_\n* 02/07/17 Improved UI behavior when performing a large batch delete of transactions. (Fx)\n* 02/06/17 An OFX import now prevents initial assignment to the wrong account type. (Fx)\n* 02/03/17 Updated to the latest Netty and JOpt Simple dependencies.\n* 02/01/17 Fixed a StringIndexOutOfBoundsException that was occurring when escaping out of a text field on MacOS. (Fx)\n* 01/30/17 Fixed a NPE that was occurring when importing transactions. (Fx)\n* 01/30/17 Corrected an OFX import regression that reduced effectiveness of detecting a duplicate import. (Swing, Fx)\n* 01/29/17 Entity trash was being checked too frequently. (Swing, Fx, GitHub Issue #21)\n\n== Release 2.28.4\n* 01/26/17 Fixed an OFX import bug.  File header was in an unanticipated format that prevented correct identification.\n\n== Release 2.28.3\n* 01/23/17 Manual was expanded with specifics of transaction entry\n* 01/20/17 Updated to the latest Hibernate and HSQLDB dependencies.\n* 01/18/17 Corrected a performance regression loading and saving bxds and zip files introduced in 2.28.0.\n* 01/17/17 Updates and  corrections to translations.  Parts of text for some languages were corrupt due to an editor bug.\n* 01/15/17 More stability improvements when under heavy background loads and using a relational database.\n\n== Release 2.28.2\n* 01/14/17 Corrected a bug with file locking on Windows OS.\n* 01/14/17 Added the Account and Amount columns to the Reminders table. (Fx)\n* 01/14/17 Corrected errors with the Polish translation. (Sławomir Szarkowicz)\n* 01/14/17 Fixed a regression that removed the Ticker/Investment column from the Investment account register. (Fx)\n\n== Release 2.28.1\n* 01/14/17 Corrected issues with inconsistent behavior of the reported memos of split transactions. (Swing, Fx)\n* 01/14/17 Updated to the latest Netty dependency.\n* 01/08/17 The Investment Register was not sizing the Quantity column correctly. (Fx)\n* 01/08/17 Fixed an IllegalArgumentException that was occurring if the option \"Next time jGnash starts\" was used when\n           dismissing the Reminders dialog. (Fx)\n* 01/08/17 jGnash now uses a priority based model for the handling of internal tasks to prevent deadlocks from\n           background operations.  This corrects some reported bugs with random freezing and hanging of the UI. (Swing, Fx)\n* 01/07/17 Updated to the latest Hibernate dependency.\n* 12/01/16 Corrected an NPE that was occurring during import of a OFX/QFX files. (Fx)\n* 11/30/16 Improved the behavior of background removal of securities history.\n\n== Release 2.28.0\n* 11/27/16 Added the transaction timestamp to the CSV export. (Swing, Fx)\n* 11/27/16 The xls and xlsx Account exports were broken by the addition of timestamps in Release 2.27.0. (Swing, Fx)\n* 11/27/16 Updated to the latest Hibernate, DynamicJasper, JasperReports and OpenCSV dependencies.\n* 11/26/16 The Account Register report was broken by the addition of timestamps in Release 2.27.0. (Fx)\n* 11/26/16 An exception was occurring if Budgeting was being used and the window was too small to display data. (Fx)\n* 11/26/16 Corrected sizing issues in the Budget interface if the screen was very wide and the budget was configured to\n           use a small number of periods. (Fx)\n* 11/26/16 Column widths are preserved so automatic column width resizing is less notable. (Fx)\n* 11/26/16 Fixed a NPE triggered by a file load while a file is already being loaded. (Fx)\n* 11/24/16 Columns were not resize correctly when adding or removing transactions. (Fx)\n* 11/24/16 Delay the upgrade notification a bit more for a cleaner startup for some users. (Fx)\n* 11/23/16 Added capability to cull down unneeded securities history as a background operation. This can reduce file\n           size and improve overall performance. (Fx)\n* 11/23/16 Improved performance when using a relational database and updating securities history. (Swing, Fx)\n* 11/20/16 jGnash would stall and appear to be hung if a large group of transactions or security history was being\n           removed when using a relational database. (Swing, Fx)\n* 11/14/16 Corrected a rare ConcurrentModificationException on systems under heavy loads. (Swing, Fx)\n* 11/08/16 Account ComboBox selection can be made using the first letter of the account. (Fx) _[Pranay Kumar]_\n* 11/08/16 Up and Down arrows can be used for selection within the Transaction number box\n* 11/05/16 Improved visual feedback for placeholder accounts and sums in the Budget interface. (Fx) (Feature Request 116)\n\n== Release 2.27.0\n* 11/05/16 Improved window positioning behavior on multi-monitor systems. (Fx)\n* 11/05/16 The Budget Goals dialog had the wrong title and layout behavior was poor. (Fx)\n* 11/03/16 Budgets results may now be display as running totals instead of per period values. (Fx)\n* 10/26/16 Improved the density and layout stability of the budget view. (Fx)\n* 10/24/16 Added selection buttons to the Reminders notification dialog to reduce required effort. (Fx)\n* 10/24/16 The Periodic Account Balance chart was improved with more selectable periods and better button names. (Fx)\n* 10/24/16 The Income and Expense bar chart was freezing due to an infinite loop. (Fx) _[Pranay Kumar]_\n* 10/24/16 Improved stability of test and build for slow or virtualized systems.\n* 10/23/16 The focus and accent colors for the Fx UI can now be changed. (Fx)\n* 10/22/16 Corrected some font scaling issues within the UI. (Fx)\n* 10/22/16 Improved column layout behavior when changing the default font scale. (Fx)\n* 10/18/16 Transaction timestamps are now strictly controlled.  Alternation of a transaction including reconciliation\n           will alter the timestamp.\n* 10/18/16 A transaction entry timestamp column has been added to the register.  It is hidden by default. (Fx)\n* 10/17/16 Prevent an IllegalArgumentException from occurring if a default or prior directory is missing when attempting\n           to select a file. (Fx)\n* 10/16/16 Added an option to control how Bayesian model matches transactions to accounts when importing. (Fx)\n* 10/16/16 Make the last selected transaction account sticky for the OFX/QIF/MT940 Import wizard. (Fx)\n* 10/14/16 The OFX/QIF/MT940 Import wizard was not displaying consistent precision for transaction amounts. (Fx)\n\n== Release 2.26.1\n* 10/13/16 Binary and XML files were not loading in the Fx interface. (Fx)\n\n== Release 2.26.0 __(File format change)__\n=== Notes:\nH2 databases will be upgraded automatically.\n\nHyperSql (*.script) databases will need to be saved to another file format in the prior release of jGnash.  They may\nbe saved back to a HyperSql database afterwards.\n\n* 10/12/16 Added an option to invert the sign of Credit, Liability, Equity, and Income accounts for the Periodic\n           Account Balance chart.\n* 10/10/16 Fixed another transaction and security history concurrency bug for relational databases. (Swing, Fx)\n* 10/09/16 The initial check for recurring transactions / reminders was taking too long. (Fx)\n* 10/04/16 Add utility method to pack and upgrade older databases. (Fx)\n* 09/07/16 Added work around for JavaFx bug JDK-8132897. Using a ComboBox was causing an application crash when running\n           on the Windows platform. (Fx)\n* 09/03/16 Improved predictability of the sort order of transactions with the same date without an assigned transaction\n           number. (Swing, Fx)\n* 08/29/16 Internal cleanup, removal of duplicated code.\n* 08/28/16 The Open dialog was not behaving correctly if a remote connection was used for the prior session. (Fx)\n* 08/21/16 Updated to the latest Hibernate dependency.  HyperSql (*.script) users will need to save to a different file\n           format before updating to this release.  You may revert back to HyperSql after the upgrade.\n* 08/21/16 Removed support for corrections to older file formats (Prior to release 2.22.0).  Existing files must have\n           been loaded with jGnash release 2.22 - 2.25 prior to using with this release.\n\n== Release 2.25.0\n* 08/20/16 JavaFx interface is now considered stable for daily use.  Remove -ea suffix off executables. (Fx)\n* 08/16/16 Fixed a rare transaction and security history concurrency bug for relational databases. (Swing, Fx)\n* 08/11/16 Running totals for Spit transaction forms were not updating correctly. (Fx)\n* 07/10/16 Command line options have changed, they now use '--' instead of '-'. See the manual for details. (Swing)\n* 07/05/16 Integrated help has been removed from the Swing interface.  Help is provided with the supplied Manual.pdf.\n         The old help system was limiting the type and quality of documentation that could be generated. (Swing)\n* 07/05/16 The Check Designer Dialog button texts were not displayed correctly. (Swing)\n* 07/04/16 The mt940 import now works with the Fx interface. (Fx)\n* 07/04/16 Plugin API has been changed to allow a plugin to support the Swing and Fx interface. (Swing, Fx)\n* 07/02/16 Plugin API implemented for the Fx interface. (Fx)\n* 06/30/16 Plugins may now be placed in $HOME/.jgnash/plugins for *nix based OS's or\n         C:\\Users\\user\\AppData\\Local\\jgnash\\plugins for Windows users. This makes upgrades easier for custom plugins.\n* 06/26/16 Plugins were not being loaded from the correct location.  This prevented the mt940 plugin from loading. (Swing)\n* 06/26/16 Added print capability and a status bar to the transaction attachment viewer. (Fx)\n\n== Release 2.24.0\n* 06/23/16 Improved performance for loading large files, working with large accounts and reports. (Swing, Fx)\n* 06/22/16 Fixed some bugs related to editing of split transactions. (Fx)\n* 06/22/16 Enable use of concatenated memos within the Swing interface. (Swing)\n* 06/19/16 Improve handling of JDBC connection errors. (Swing, Fx)\n* 06/19/16 The Fx UI would not completely shut down in some rare instances. (Fx)\n* 06/18/16 Shutdown of server was not working correctly. (Fx)\n* 06/17/16 Implemented use of command line options for the Fx interface.  (Fx)\n* 06/09/16 Implemented the Payee Pie chart for the Fx interface.  (Fx)\n* 06/02/16 The Income and Expense Pie chart now uses a DoughnutChart variation. (Fx)\n* 05/27/16 Update to the latest Netty and include only the needed dependencies. (Swing, Fx)\n* 05/26/16 Improved the name of the application in the OSX and Gnome global menu. (Fx)\n\n== Release 2.23.0\n* 05/14/16 Added the Periodic Account Balance report. (Fx)\n* 05/04/16 Implemented the Reminders dialog for the FX interface. (Fx)\n* 05/03/16 Implemented the \"Shutdown Server\" command for the FX interface. (Fx)\n* 05/03/16 Added access to the Budget Manager from the Tools Menu. (Fx)\n* 05/02/16 Implemented \"File | Save As\" capability for the FX interface. (Fx)\n* 05/02/16 Implemented Password Change capability for relational databases. (Fx)\n* 05/01/16 Modified transactions were not refreshing consistently in the register table. (Fx)\n* 04/26/16 Fixed import of an account tree when using a relational database. (Swing, Fx)\n* 04/25/16 Added Account structure import and export capability. (Fx)\n* 04/24/16 Display a wait indicator when a generating a large report. (Fx)\n* 04/24/16 Added the Account Register Report. (Fx)\n* 04/24/16 Added a Memo specific column to the investment register table and separated the Investment column (Fx)\n* 04/17/16 Added an option to the split entry dialog to automatically concatenate the memos of split transactions.\n         This will reduce file size if used and reduces split transaction entry effort. (Fx)\n* 04/15/16 Display a message at startup when a newer version is available for download. (Swing, Fx)\n* 04/10/16 Language files now use UTF-8 file encoding. (Swing, Fx)\n\n== Release 2.22.1\n* 04/03/16 Fix for Fx UI font scaling issues for locales that use a comma for the decimal separator. (t-pa)\n* 03/29/16 Preserve the tree structure in budget exports. (t-pa)\n* 03/28/16 Fixed random deadlocks when loading budgets in the Swing interface. (t-pa)\n* 03/28/16 Corrected budget calculations for mixed child account types. (t-pa)\n* 03/28/16 Added Polish translation (Sławomir Szarkowicz)\n* 03/22/16 Fixed broken OFX export.\n* 03/09/16 Correct issues with table column widths sizing themselves incorrectly. (Fx)\n* 03/07/16 Budgets were not calculating net period amounts correctly for income and expense accounts. (Bug #216) (Swing, Fx)\n* 02/28/16 Enable automatic load of the last file for the Fx interface. (Fx)\n* 02/28/16 Force the Fx interface on Windows to use 95% font scaling for work around potential Hi-DPI display bugs. (Fx)\n* 02/28/16 NPE was occurring when editing transactions with an empty payee or memo's. (Fx)\n* 02/28/16 OFX/QFX files with a capitalised file extension were not visible for import. (Fx)\n\n== Release 2.22.0 __(File format change)__\n* 02/20/16 Fixed behavior of manual date input.  It would sometimes reposition the caret and ignore input. (Fx)\n* 02/18/16 Fixed a bug that was preventing removal of stale data from the relational database file formats.\n* 02/14/16 Changed storage format for Budgets\n* 02/09/16 Enable Menu mnemonics for platforms that support it (Fx, Windows).  Changed mnemonics design so it is easier\n         for translation and works for both Swing and Fx interfaces.\n* 02/08/16 Added the Net Worth Report. (Fx)\n* 02/06/16 Added the Balance Sheet Report. (Fx)\n* 02/05/16 Added the Profit Loss Report. (Fx)\n* 01/29/16 Added the Portfolio Report. (Fx)\n* 01/29/16 The running balance in the register was not updating correctly with transaction changes. (Fx)\n* 01/25/16 The transaction number ComboBox would not always capture a manually entered value. (Fx)\n* 01/18/16 Incorrect accounts were available for selection in the account ComboBox. (Fx)\n* 01/10/16 Added the Monthly Account Balance CSV export report to the jGnashFx UI. (Fx)\n\n== Release 2.21.0\n* 01/09/16 Fixed a bug that was causing Investment Accounts to loose Securities resulting in exceptions.  It was triggered\n         when a Security was added to more than one Investment Account and only users of a relational database would be\n         impacted. Files will be repaired automatically (Swing, Fx)\n* 01/06/16 Fixed a regression that was preventing creation of new Reminders. (Fx)\n* 01/06/16 The Reminder table was not updating correctly after a new recurring event had occurred. (Fx)\n* 01/04/16 The Account type was being corrupted for top level accounts when editing properties. (Fx)\n* 01/04/16 The default account type was not the same as the parent when creating a new account. (Fx)\n* 12/27/15 Set an explicit and stable sort order for budget account groups. (Swing, Fx)\n* 12/14/15 Added an Income / Expense Bar Chart report to the jGnashFx UI. (Fx)\n* 12/13/15 Month labels for tabular reports were off by one. (Swing)\n* 12/13/15 Added the Profit and Loss text report to the jGnashFx UI. (Fx)\n* 12/12/15 Added an Income / Expense Pie Chart report to the jGnashFx UI. (Fx)\n* 12/12/15 The Profit and Loss text report was failing to execute. (Swing)\n* 12/08/15 An option to remember the last used transaction tab has been added. (Fx)\n* 12/07/15 An option to change button order has been added if you do not like the OS default. (Fx)\n* 11/27/15 The budget view will automatically focus the current period when first displayed. (Fx)\n* 11/27/15 Fixed an IndexOutOfBounds exception when reducing the displayed period count for a budget. (Fx)\n* 11/27/15 Improved column sizing for the account summary table within the budget view. (Fx)\n* 11/26/15 Improved general dialog sizing and positioning behavior. (Fx)\n* 11/24/15 The transaction register may now be filtered/searched by date, reconciled state, memo, and payee. (Fx only)\n\n== Release 2.20.0\n* 11/22/15 Fixed several potential locale specific bugs.\n* 11/17/15 Added a context menu to the account tree table. (Fx)\n* 11/16/15 Right aligned decimal values in the account tree table. (Fx)\n* 11/16/15 Fixed the account code editing behavior from within the account tree table. (Fx)\n* 11/16/15 Completed implementation of Budgeting for the jGnashFx UI.\n* 11/15/15 Reduced distribution size by excluding compile time dependencies.\n* 11/11/15 Budgeting now uses the ISO 8601 standard for handling weekly periods for consistency. (Swing)\n* 11/11/15 Fixed several budgeting bugs related to 53 week years. (Swing)\n* 11/10/15 Fixed an error that would occur when creating a new file and the given filename extension did not match the selected file type. (Fx)\n* 11/10/15 Duplicate tabs were being created and an exception thrown when creating a new file. (Fx)\n* 11/05/15 Added the ability to change the default date format to something other than the locale default. (Swing, Fx)\n* 10/31/15 Fixed a file version check bug that was causing asset and liability accounts to lose their budget visibility.\n* 10/31/15 Internal test framework should not leave files lying around anymore.\n* 10/28/15 Fixed a bug with account combos not retaining their initial value. (Fx)\n* 10/27/15 Improved font appearance by forcing smoothing type to gray. (Fx)\n* 10/25/15 The last used tab of the primary interface is now restored at startup. (Fx)\n* 10/24/15 Windows were not saving their size and location because of a race condition. (Fx)\n* 10/24/15 Transaction number combo box was not working correctly. (Java Bug JDK-8136838 Fx)\n* 10/18/15 Improved the column packing speed of the transaction register. (Fx)\n* 10/17/15 The base font size will need to be reset after some code cleanup. (Fx)\n* 10/17/15 Avoid extraneous automatic securities price updates during the weekend if at least one has occurred.\n* 10/14/15 Fixed a race condition in the account ComboBox resulting in NPE when creating a new account. (Bug #212) (Fx)\n* 10/14/15 Increase the darkness of the alternating tabular data row color from 2% to 6%. (Fx)\n* 10/11/15 Added keyboard accelerators. (Fx)\n\n== Release 2.19.0\n* 10/10/15 Cleaned up Transaction Import API.  External import plugins will need to be updated.\n* 10/09/15 Updated to latest Netty release\n* 10/08/15 Improved layout behavior for investment transaction forms. (Fx)\n* 10/07/15 Fixed a transaction entry bug when selecting the next available check number. (Fx)\n* 10/05/15 Improved register layout. (Fx)\n* 10/05/15 The reconcile button in the accounts list was not working. (Fx)\n* 10/04/15 Fixed an NPE that could occur when creating a new account. (Fx)\n* 10/04/15 Icons behave better when the base color and font size is changed. (Fx)\n* 10/04/15 Implemented QIF import for the jGnashFx UI.\n* 09/25/15 The QIF import utility has been improved to make a better determination of the date format automatically.\n* 09/25/15 Some QIF imports would fail because of a date parsing regression in 2.17.0.\n* 09/20/15 Fixed an OFX and QIF bug that was preventing matches of previous and manually entered transactions. (Swing, Fx)\n* 09/20/15 Fixed an exception if an attempt was made to import an OFX or QIF file with a previously imported transaction. (Swing, Fx)\n* 09/20/15 Implemented OFX import for the jGnashFx UI.\n* 09/15/15 Fixed an exception when opening the Transaction Number configuration Dialog (Fx)\n* 09/15/15 Added XLS, and XLSX files to the existing export capability of the transaction register (Swing)\n* 09/15/15 Added CSV, OFX, XLS, and XLSX file export capability to the transaction register (Fx)\n* 09/14/15 Fixed ellipse mark that made it into the Open toolbar button (Swing)\n\n== Release 2.18.0\n* 09/13/15 jGnashFx Early Access is now included with the distribution.\n* 09/08/15 The NetWorth and BalanceSheet reports were not including Simple Investment account types.\n* 09/06/15 Fixed QIF date parsing import bug introduced by 2.17.0\n* 09/01/15 (FX) DatePicker now increments and decrements with use of vertical and horizontal scroll input\n* 08/29/15 Remove support for importing jGnash 1.x files\n\n== Release 2.17.1\n* 09/01/15 Fix for a one day shift when converting Dates to LocalDates for XML and BXDS file formats.\n\n== Release 2.17.0 __(File format change)__\n* 08/28/15 Automatic backup preferences are now stored within the data file.  This is better for users working off of\n         portable storage and multiple computers.  You will need to update your preferences with this release.\n* 08/24/15 Securities historical charts now factor in stock splits and reverse splits.\n* 08/23/15 The JavaFx UI for Securities history allows manual edits of split and dividend history.\n* 08/16/15 File formats have changed and will not be backwards compatible with prior releases.\n* 08/16/15 Added framework for handling historical information for stock splits and dividends. (File format change)\n* 08/15/15 Migrated to use of the new Java 8 LocalDate classes.  This improves the overall application performance. (File format change)\n* 08/12/15 Added RTF, and DOCX export capability for tabular reports.\n* 08/12/15 Updated to the latest DynamicJasper.\n* 08/11/15 Removed unused dependencies from the distribution files and build system.\n* 08/02/15 Dependencies updates.\n* 08/02/15 Temporally disable SSL jdbc connections until some bugs are sorted out.\n* 08/02/15 Encrypt client/server communications if a password is specified without requiring explicit enabling of encryption.\n* 07/31/15 Fixed a bug that would cause transfers of attachments in client/server mode to fail under Windows OS\n* 07/26/15 The exchange rate dialog was not showing the close button and the clear button was in the wrong location.\n* 07/22/15 Fixed a rare concurrency issue when updating securities history\n* 07/20/15 Fixed an issue with historical investment downloads timing out when using a relational database.\n* 07/11/15 Fixed issues when exporting an account structure when using a relational database.\n* 07/10/15 Removed the days past due field in recurring form.  It's not needed because we have a Due date column now.\n* 07/09/15 Add Last Posted date and Due date columns to the reminder table for easy reference\n* 07/09/15 Add new capability to create a new recurring transaction from an existing transaction (context menu in the register)\n\n== Release 2.16.0 __(Java 8)__\n* 07/03/15 Dependencies updates, fixes for some Hsqldb database issues and minor performance improvements.\n* 06/26/15 General cleanup and internal changes to support the new FX user interface in development.\n* 05/31/15 An exception would occur if a Security was removed immediately after it was created and loaded with history (Bug #208)\n* 05/31/15 Changed the reconcile checkbox to support three states for not-reconciled, cleared, and reconciled.\n* 05/25/15 Return of Capital transactions were not being shown in the register table correctly.\n* 05/14/15 Autocomplete now makes better choices for debit and credit transactions.\n* 03/14/15 Ensure directory has been created first before trying to write a file.\n* 03/14/15 Historical import dialog for securities did not correctly preset the prior month as intended.\n* 03/14/15 Java 8 is now required for 2.16.0 and newer\n\n\n== Release 2.15.2\n* 02/12/15 Add tooltip to the investment gains and fees details buttons\n* 02/09/15 SecurityHistoryNodes are now immutable to prevent database corruption\n* 02/08/15 Insure resource cleanup if an SQL error is thrown\n* 01/31/15 Fix for potential resource leak when exporting budgets to a spreadsheet\n* 01/24/15 Fix sorting issues with securities and currency history dialogs\n* 01/23/15 Initial sort order for security history was incorrect for XML and BXDS file formats\n* 01/22/15 Dependencies updates, fix for some H2 database and Hibernate warnings\n\n== Release 2.15.1\n* 12/24/14 Fixed import of an exported account tree\n* 12/24/14 Fixed security price import from Yahoo UK\n\n== Release 2.15.0 __(File format change)__\n* 12/07/14 Display a warning dialog if loan amortization is not configured instead of logging to the status bar.\n* 12/03/14 Correct database at load if a transaction was incorrectly marked as orphaned and removable.\n* 11/26/14 The simple investment account type was moved to it's own group to improve program logic\n* 11/23/14 Active Account Securities were not marked to prevent removal in the Account Properties dialog.\n* 11/22/14 Updated Spanish translation (Marcelo Abeldaño)\n* 11/21/14 Reminder transactions were being incorrectly identified as orphaned.\n* 11/12/14 Improved sort capability.  Accounts are now sorted by an account code and then by name.\n* 11/11/14 Added a Code property to Accounts. Codes can be change to suit users needs. (Changes file format)\n* 11/09/14 Improved reconciliation behavior.  Reconcile Settings are remembered from prior sessions and are intelligently updated.\n* 11/09/14 Add sorting capability to the reconcile dialog tables.\n* 11/08/14 A dialog will now be displayed when the file has been automatically upgraded and a backup of the old version made.\n* 11/08/14 Relational database files will be altered automatically to address Hibernate Bug #HHH_9389\n* 11/06/14 Settings for background updates of exchange rates and securities were moved into the data file (Changes file format)\n* 11/06/14 Reconcile settings were moved into the data file for consistent behavior when the file is shared on multiple\n         systems (Changes file format)\n\n== Release 2.14.1\n* 10/31/14 Fixed a bug that was preventing Securities history from being deleted if added within the same jGnash session\n         for relational databases.\n* 10/31/14 Updated to latest Hibernate 4.3.7 release\n* 10/28/14 Minor translation improvements\n* 10/26/14 After exporting a budget to XLS, you can left align a numeric column to see indents.\n* 10/26/14 Fixed a bug with an empty account being changed into a placeholder account and retaining invalid budget goal\n         information.  Placeholder accounts should only roll-up child account goals.\n* 10/26/14 Bug fix for placeholder accounts not recalculating balances correctly if their currency is changed.\n* 10/24/14 Improve shutdown behavior when interrupting background updates.\n* 10/22/14 Fixed another race condition that could freeze the UI at startup\n* 10/21/14 Fix for incorrect totals for register reports with split details shown.  The sum of the split was being\n         calculated twice.  Correct behavior is to not show the sum of the splits.\n* 10/21/14 Updated to the latest dependencies for report generation and XLS file exports.\n\n== Release 2.14.0\n* 10/19/14 PDF version of the integrated help is now packaged with the zip distribution.\n* 10/14/14 When using the reconcile Wizard, Finish Later will now mark the transaction as Cleared and not Reconciled.\n* 10/12/14 Redesigned the reconcile behavior to use the statement end date. Public and internal API's have changed for\n         reconciliation and may break plugins.\n* 10/12/14 Committing reconcile changes can take a long time when working remotely or using a relational database.\n         Improve the UI behavior by showing a wait message instead of freezing the display.\n* 10/12/14 Changing the reconciled state of a transaction using the context menu was not following the rules of the\n         selected register option.\n* 10/05/14 Bug fix for potential return of an incorrect closest by date market price for a security\n* 10/05/14 Bug fix for potential erroneous removal of the prior days security history during a market price update\n* 10/04/14 Bug fix for difficult to trigger Concurrent Modification error when updating stock prices\n* 09/26/14 Bug fix for false positives identifying duplicate transactions when importing QIF files.\n* 09/25/14 Handle non-standard OFX files that use commas as a decimal separator for amounts\n\n== Release 2.13.6\n* 09/21/14 Updated to latest Insubstantial release.  This fixes the Substance look and feel compatibility with Java 8\n* 09/20/14 Updated to the latest JGoodies dependencies.  This should improve font appearance on Windows systems in some instances\n* 09/20/14 Fixed the build process for the mt940 plugin so it always stays current\n* 09/07/14 Internal cleanup, improve relational database load behaviors\n* 07/29/14 Fixed a race condition that would cause a random NPE when loading security histories from a relational database\n* 04/28/14 Force eager load of budget goals to prevent a random NPE at file load when using a relational database\n* 04/28/14 Updated to the latest Netty\n* 04/17/14 Updated to latest H2 database release\n* 04/17/14 Updated to latest Hibernate 4.2.x release\n\n== Release 2.13.5\n* 02/23/14 Fixed an NPE when cleaning out orphaned transactions from a prior jGnash bug\n* 02/22/14 Update to the latest HSQLDB database release\n* 02/22/14 Update to the latest H2 database release\n* 02/22/14 Update to latest XStream, security vulnerability CVE-2013-7285, an arbitrary execution of commands when unmarshalling\n* 02/09/14 Minor API changes to allow operation using Java 8\n* 01/25/14 Fixed a rare ConcurrentModificationException that would occur when updating stock prices.\n* 01/12/14 Fixed another race condition that could freeze the UI at startup if loading a very large file.\n* 01/12/14 Any newly added or modified transactions will be highlighted in the register table for easy identification.\n* 01/11/14 Fixed a race condition that was preventing newly duplicated transactions from gaining focus in the register.\n* 01/11/14 When an account register was open in it's own window, window focus could be lost when deleting and duplicating\n         transactions.\n\n== Release 2.13.4\n* 01/01/14 Accounts appearing in the budget model now respect the budget visibility of the ancestor accounts.\n* 01/01/14 Fixed a bug with encrypted file attachment transfers\n* 12/31/13 Fixed a race condition that could hang the UI at startup when loading a large file.\n* 12/31/13 In some cases, a file would not reopen if a relational database was not closed cleanly.\n* 12/27/13 Update to the latest XStream\n* 12/26/13 Improve font appearance when running under KDE\n* 12/21/13 Correct Budget UI exceptions that were occurring when performing SaveAs operations.\n* 12/15/13 Show full currency formatting in the Budget display, otherwise, currency of the account is not obvious.\n* 12/15/13 A Java bug was preventing new files from being created if a default currency was not determinable.\n* 12/12/13 Changed the name of the Budget Column from \"Change\" to \"Actual\" to clarify intent.\n\n== Release 2.13.3\n* 12/05/13 Client/Server communications are now fully encrypted if enabled from command line for supported locales.\n* 11/29/13 Allow loading of a file with duplicate Config objects.  The file will be corrected at load time.\n\n== Release 2.13.2\n* 11/17/13 A caching bug was causing the first transaction added to an account after restart of jGnash to show up twice.\n         After restart the duplicate transaction would go away.\n\n== Release 2.13.1\n* 11/12/13 Update to latest Netty, H2, and Hibernate dependencies.  Users using H2 database may notice more consistent\n         shutdown times.\n* 10/29/13 Fix a race condition that was causing an ArrayIndexOutOfBoundsException in the GUI when\n         adding a new transaction.\n\n== Release 2.13.0\n* 10/15/13 Correctly report and handle an attempt to open a wrong file type. (Bug #206)\n* 10/15/13 Correctly report an attempt to open a directory instead of a file. (Bug #205)\n* 10/02/13 Excess UI updates could occur when updating a budget goal and create performance issues.\n* 10/02/13 Fixed an exception that would occur when filling in a bi-weekly budget.\n* 08/13/13 Improved UI performance of the busy indicator on slower machines (Klemen Zagar)\n* 08/11/13 When saving a compressed backup on exit, use the OS's temporary directory to play nice with cloud services (Patch #55, Klemen Zagar)\n* 07/10/13 Update to the latest JFreeChart\n* 06/29/13 Added a new feature; Transactions may now have image attachments.\n* 06/20/13 New client server architecture based on Hibernate/JPA2 with H2 or HSQLDB SQL database.  db4o support\n         has been purged from the code base.\n* 06/10/13 Improve dialog positioning when using multiple monitors and when using fewer monitors than the last run.\n* 04/23/13 Use Netty instead of Mina for performance and for improved protocol support\n* 04/21/13 Added a Money Market account type.\n* 04/20/13 New Engine and account api for setting and accessing custom text based account properties.\n* 04/20/13 File schema changes to support external links to files and custom tags for transaction entries.\n* 04/04/13 Discover and remove orphaned transactions left behind when Reminders were removed.\n* 04/03/13 Improve the shutdown experience if a file is not open.\n* 03/30/13 Added a new command line option to enable the xrender pipeline for X11 based systems.\n* 03/28/13 Prevent background updates from running during a shutdown if performed right after startup.\n* 03/25/13 Create a versioned backup of the old file automatically if the file format has been changed.\n* 03/21/13 Changed binary and xml file structure for amortization objects.\n\n== Release 2.12.0\n* 03/03/13 When importing transactions, display a tooltip for payee and memo fields to make transaction determination easier. (Feature Request #107)\n* 03/02/13 Automatically update the exchange rate tables when a multi-currency transaction is entered for a given date if one has not been set.\n* 03/01/13 Updated to DynamicJasper 4.0.3.\n* 02/24/13 Corrections made to the Portuguese translation. (Fernando Ribeiro da Silva)\n* 02/14/13 Updated the jGoodies libraries.\n* 02/12/13 Updated to XStream 1.4.4.\n* 02/12/13 Updated to SwingX 1.6.5.\n* 02/12/13 Updated to Apache POI 3.9.\n* 02/10/13 Changed the exit process so that the final file write and closure is complete before the UI disappears instead of afterwards.\n* 01/30/13 Improved the natural sort order of investment transaction for improved consistency (Date, Type, Memo, Security, Modification Date, Internal Id)\n* 01/20/13 Fix for IllegalArgumentException caused by reordering table columns\n* 12/09/12 Added register option to restore the last used transaction tab\n* 12/09/12 When modifying an existing account, the current account currency would not be set in the dialog correctly.\n* 12/02/12 Update to latest Substance L&F\n* 11/25/12 Make confirmation on transaction deletion the default.\n* 11/24/12 Added basic OFX export of accounts (Investment accounts are still a work in progress)\n* 11/23/12 Warn if you are using a db4o (jdb) and recommend that you save as another format\n* 11/10/12 Added CSV export direct from the transaction register.\n* 11/10/12 Backup files were not being created in the same directory as the data file.\n* 11/04/12 Update to Mina 2.0.7\n\n== Release 2.11.0\n* 10/24/12 Tabular style reports will start with a better default page size the first time the report is run.\n* 10/23/12 Reminder dates were not correct if it was modified after being executed.\n* 10/21/12 Display a message if an error occurs during a budget export (Read only file, etc)\n* 10/20/12 Reworked the Balance Sheet report.  Results are displayed by period instead of a running balance and retained\n         income / expense is calculated.         \n* 10/14/12 Added a new Simple Investment account type.  This can be used for Annuities or Guaranteed Retirement accounts\n         that you cannot actively manage. \n* 10/14/12 Added a utility script that can be run to remove weekend security history.\n* 10/14/12 Update to the latest Insubstantial/Substance L&F release.\n* 10/13/12 Improve security price import from Yahoo.  Dates returned from Yahoo are now used.  This prevents\n         history entries on weekends and financial holidays.\n* 10/13/12 Corrections to the reporting in the Income and Expense by Payee pie chart report as well as GUI\n         behavior improvements.  It now has a chart for debit and credits. (Pranay Kumar)\n* 10/13/12 Updated DynamicJasper to the latest release.\n* 10/11/12 Updated XStream and Mina dependencies to the latest releases.\n* 10/08/12 Add new controls to the historical security import dialog to make selection of securities faster and easier.\n* 10/07/12 The Income and Expense pie chart now displays the default currency in addition to the account\n         currency when multiple currencies are being used. (Pranay Kumar)  \n* 10/06/12 Added an option for matching to the last similar entry when entering transactions. (Pranay Kumar)\n* 10/03/12 Updated the Spanish Translation. (Marcelo Abeldaño)\n* 09/03/12 A exception would occur when trying to generate a loan payment with a zero percent interest rate.\n\n== Release 2.10.0\n* 09/02/12 The Jump button would not work from a register in a separate window (Bug #3563951)\n* 09/02/12 Do not preload report fonts to reduce startup time and reduce memory usage if reports are not being generated.\n* 08/28/12 Changed busy indicator for significant memory usage reduction.\n* 07/17/12 Fix for printing checks on Windows printers.\n* 07/12/12 Dividends were not showing a correct value in the register total column (Bug #3526172)\n* 07/12/12 Code migrated to fully utilize Java 7 try-with-resources.\n* 07/07/12 Fixed a memory leak that was occurring when loading plugins\n* 05/27/12 Added workarounds for JVM bugs when using Gnome 3 and Cinnamon. Mouse behavior was not correct when the jGnash\n         window was maximized.\n* 05/18/12 Imported transactions are automatically assigned an account using a Naive Bayes classifier.\n* 05/17/12 Improved imported transaction match against manually entered transactions\n\n== Release 2.9.0\n* 05/03/12 Check for Java 7 or newer before executing\n* 05/02/12 Added an alternating pattern fill option to the budget goal entry dialog\n* 04/26/12 Strip extra white space when importing OFX files\n* 04/24/12 Warn if an attempt is made to modify a transaction with a locked account\n* 04/24/12 Correctly handle the modification of a transaction against a hidden account\n* 04/24/12 Mark newly imported QIF transactions so they can be considered for account matching (no change to file format)\n* 04/24/12 The account tree would not display correctly after a new file was created until open and closed.\n* 04/24/12 Make the new binary format the default for new files.\n* 04/23/12 A new file would not be created if the specified directory did not exist.  jGnash will now create the\n         directory tree automatically.\n* 04/09/12 Added new fast and compact binary file format\n* 04/07/12 Added a Smart fill panel to the budget goal entry dialog for historical entry and fill all\n* 03/18/12 Fixed the import of Citibank QFX and OFX credit card exports.\n* 03/17/12 Modularized jGnash into several Maven modules and separated the UI code from the core engine code\n* 03/17/12 jGnash was causing Java 7 JRE to seg-fault on close.\n\n== Release 2.8.0\n* 03/10/12 Help build system no longer requires OS level installed dependencies\n* 03/05/12 Update to Insubstantial 7.1 and the latest JGoodies dependencies\n* 03/05/12 Fixed an NPE that would randomly occur at startup\n* 03/05/12 Corrected budget UI controls state when adding a budget for the first time and deleting the last budget\n* 03/04/12 Mavenized the help build system\n* 02/15/12 Printable reports can now be saved as xls files\n* 02/15/12 Improved mt940 import (Patch #3487030, Arnout Engelen)\n* 02/14/12 Fixed issue with large budget values being clipped in the budget UI\n* 02/13/12 Working xls and xlsx export of budget results\n* 02/12/12 Improve handling of multiple currencies in the budget UI\n\n== Release 2.7.0\n* 02/08/12 Added functionality to sort the Profit and Loss report by Account balance and percentiles (Patch #3154343, Klemen Zagar)\n* 02/06/12 Removed duplicate code in budget results UI\n* 02/05/12 Fixed formatting of the creation date on printed and pdf reports\n* 02/05/12 Updated to latest DynamicJasper and associated dependencies\n* 02/05/12 Reduced complexity of the budget results UI code and eliminated redundant listeners\n* 02/04/12 Rewrote the budget results calculation code\n* 02/02/12 Updated Dutch translation (Patch #3482860, hellemans)\n* 02/01/12 Transactions may now be modified through arrow key selection inside the register (Patch #3481312, hellemans)\n* 01/29/12 Reworked the summary information for the budget view including the addition of a row footer and options\n         to display the summary information\n\n== Release 2.6.2\n* 01/21/12 Set the jGnash file filter as the default when choosing a file\n* 01/21/12 Improve budget UI performance when transaction event and budget changes occur\n* 01/19/12 Budget results would randomly show 0 if the CPU was heavily loaded\n* 01/15/12 Fix generation of weekly and bi-weekly budget dates for non-US locales; Do not assume Sunday is\n         the first day of the week.\n* 01/11/12 Budget totals were calculated incorrectly after a budget's properties/period were modified\n\n== Release 2.6.1\n* 01/08/12 A default user and password is now set if not specified when using client / server functionality\n* 01/08/12 Add a Yearly period option for Budgets\n* 01/08/12 Add a command line option to help detect UI code that hangs the EDT\n* 01/07/12 Corrected some UI update and threading and performance issues with the Budget interface\n* 01/03/12 Reinvested dividends were not showing a correct value in the register total column (Bug #3467513)\n* 01/02/12 Close any open windows first when closing a file\n* 01/01/12 Switched build system over to Maven and Ant hybrid\n* 12/28/11 Expand budgeting help for budget properties\n* 12/28/11 Add functionality to control account types for a budget (income, expense, asset, liability)\n* 12/28/11 Selected budget year was not be used when editing goals and switching between budgets\n* 12/26/11 Update to the latest, JGoodies, XStream, Mina, JFreeChart external dependencies\n* 12/25/11 Update to the latest args4j external dependencies\n\n== Release 2.6.0\n* 12/24/11 Add help content for the new budget feature\n* 12/14/11 Additional fixes for hierarchical display of the budget\n* 12/13/11 Improved performance when working files with large account structure and many transactions.\n* 12/11/11 The Budget account structure was not consistently updating when accounts were added, remove, or changed.\n* 12/04/11 The total remaining for budgets periods was not calculated correctly (Chris Bunney)\n* 12/03/11 Add ability to break budgets and goals down to daily entry if desired\n* 12/01/11 Improve editing and focus behavior when changing budget goals\n* 12/01/11 Fix for NPE occurring with Metal look and feels\n* 11/26/11 Minor internal cleanup\n* 11/22/11 Use the meta key instead of the control key on OSX systems\n* 11/20/11 Completed fully functional hierarchical display for budgets\n* 11/12/11 Minor improvements for behavior and appearance when running on OSX\n* 10/25/11 Sum of transactions shown in the tooltip was not correct if the register was sorted.\n* 10/17/11 Investment transaction total values were not displayed correctly in the register Total column (Bug #3408123)\n* 10/15/11 Yahoo UK historical download address changed (Bug #3423566)\n* 10/15/11 Improved behavior of auto completion. Added an option to control the case sensitivity of the match. Don't replace\n         the memo or amount and account selection if entered before the payee field is matched. (Bug #3407399, #3407400)\n* 10/07/11 Balance reversal selection was not being restored correctly in the option dialog (Bug #3417960)\n* 10/04/11 Fixed OpenJDK specific bugs\n* 09/15/11 Second period of the displayed budget was missing\n* 09/14/11 Fix bug with exceptions occurring in the budget interface when the account structure changed\n* 09/07/11 Improved overall UI layout for the new budget interface\n* 09/06/11 Internal code cleanup, PMD, etc.\n* 09/05/11 Menu items for Substance look and feels were not being selected when active (Bug #3404037)\n* 09/04/11 Fix for enabled symbol when a substance look and feel is used (Bug #3403710)\n* 09/04/11 Improve the behavior of the help dialog (Feature Request #3174487)\n* 09/04/11 Add a double click listener for modifying reminders (Feature Request #3403673)\n* 09/04/11 Add a delete key listener for reminders (Feature Request #3403736)\n* 09/03/11 Add Sparklines to the budget display\n* 09/01/11 Update default Portuguese accounts (Pietro A R CERCHIARI)\n* 08/29/11 Update Italian translation (Davide)\n* 08/26/11 Added a property to accounts to exclude them from budgets\n* 08/26/11 Added a field to the account properties dialog for a long hidden bank id property\n* 08/17/11 Fix a bug with UI actions not working when running from a jar file\n* 08/14/11 Add a summary footer to the budget view\n* 08/07/11 Remove locale specific information from CurrencyNode.  db4o cannot persist Java 7 Locale correctly and\n         the Locale specific information has not adding value.\n* 08/05/11 Fix Comparator so it plays nice with Java 7 (Exception: Comparison method violates its general contract!)\n* 08/03/11 Make the current period visible by default in the budget view\n* 08/02/11 Do not show hidden or locked accounts in the budget view\n* 08/02/11 Do not show hidden accounts in the account selection combo boxes (Feature Request #3384937)\n* 08/02/11 Show a tooltip in the budget views account header with the full account path\n* 07/31/11 Added function to create a new budget based on historical data.\n* 07/21/11 Fundamentals of a budgeting system are working.\n* 07/21/11 Fixed a bug with the mt940 import plugin that was causing an exception if a file was not open instead of\n         disabling the plugin until a file is loaded.\n* 07/14/11 Fix for OFX import when preceding spaces are in the transaction amount\n* 06/19/11 Update to latest JGoodies libraries\n* 02/16/11 Check for multiple root accounts and correct if needed at startup.\n* 02/15/11 Fixed a bug where an account would show twice in reports in very rare circumstances.\n* 02/13/11 Minor selection and expansion performance improvement for the account view.\n* 01/22/11 Corrected layout issues in the investment transaction entry forms\n* 01/18/11 Mnemonics for menu items were not being shown\n* 01/16/11 Base API for Budgets added to the engine\n* 01/16/11 Use Annotations to reduce amount of managed code for UI actions\n\n== Release 2.5.1\n* 01/02/11 Added new option to change the font size of the Nimbus Look and Feel\n* 01/02/11 Reorganized the Options Dialog to reduce the required space for small displays\n* 01/02/11 Added option to control network connection timeouts\n* 12/31/10 Add new variation of the Monthly Account Balance report (Patch #3087286, Pranay Kumar)\n* 12/31/10 Dumped the jGnash.app OSX launcher... sometimes it works, and sometimes it does not depending on the\n         age of the system.  Will now leave it up to the end user to sort it out. (Bug #3148438, Peter B. West)\n* 12/31/10 Improve behavior of split entry dialog (Bug #3132102, Chris B)\n* 12/31/10 jGnash 1.x import fixes and performance improvements (Bug #3147017, Klemen Zagar)\n* 12/30/10 Code cleanup efforts\n* 12/30/10 Protect against a null locale when importing jGnash 1.x file (Bug #3147015, Klemen Zagar)\n* 12/30/10 Protect against an invalid file entry (Bug #3147013, Klemen Zagar)\n* 12/30/10 Protect against NPE (Bug #3147012, Klemen Zagar)\n* 12/05/10 Improve the performance of the Accounts list for large account structures and play nice with db4o 7+\n* 12/05/10 Ensure XML background write thread is complete before another write can occur or jGnash can close (Bug #3071371)\n* 11/28/10 Don't freeze the UI when duplicating a transaction on slow systems.\n* 11/28/10 Update to SwingX 1.6.2\n* 11/16/10 Fix poor button layout for wizard dialogs\n* 10/18/10 Fix handling of the exchange rates for the pie chart report (Patch #3089661)\n* 10/17/10 Protect against incomplete XML file writes\n* 09/26/10 Enable selection of an account in the accounts tree by pressing the first letter of the account name\n\n== Release 2.5.0\n* 09/19/10 Added additional integrated help content.\n* 09/18/10 Improved error handling when the selected font size for a report is too large.\n* 09/12/10 Added new options to reverse the display of account balances (Patch #2935203, Peter Vida)\n* 09/12/10 When opening an income account, select the income tab by default (Feature Request #2889091)\n* 09/08/10 Cleaned up a console warning when displaying reports.\n* 09/06/10 Reinvested dividend transaction fees were not being handled correctly. (Bug #2924555)\n* 09/02/10 The exchanged amount in a multi-currency transaction would not be correct if a change in\n         field focus had not occurred (Bug #3045847)\n* 09/01/10 A Stack overflow was occurring when adding a new loan payment (Bug #3053384)\n* 09/01/10 Accounts were not always visible when choosing from a dialog\n* 08/31/10 UI components would not display correctly on OSX after integration of the Substance Look and Feel\n* 08/29/10 Mt940 import converted to a jGnash Plugin\n* 08/29/10 Finalized new Plugin API\n* 08/27/10 Pieces of the Portuguese translation were missing\n* 08/21/10 Update to Substance 6.1\n* 08/21/10 Reports would not show if a default font was not available (Bug #3050057)\n* 08/11/10 The color for reconciled balance in the account list view was not always correct (Bug #3040309)\n\n== Release 2.4.1\n* 07/21/10 Added CTRL-F4 shortcut to close the active register window (Feature Request #2889093)\n* 07/21/10 Added an option to disable the Substance Look and Feel animations\n* 07/21/10 The report print button would not work when using the Substance Look and Feel\n* 07/21/10 Updated to the latest DynamicJasper and JasperReports\n\n== Release 2.4.0\n* 07/18/10 Add functionality to adjust the global font size when using the Substance look and feel\n* 07/18/10 Add Startup option to control automatic load of the last open file (Feature Request #2933793)\n* 07/18/10 Improve duplicate transaction functionality (Feature Request #1683578)\n* 07/15/10 Fix for a random NPE occurring at startup (Bug #3020688)\n* 07/12/10 Update to SwingX 1.6.1\n* 07/12/10 Reworked the validation framework to use JXLayer\n* 07/11/10 Integrate JXLayer into the UI to improve effects and behavior\n* 07/09/10 A Portfolio report column name was not being displayed correctly\n* 07/07/10 The expansion state of the account list view is now restored on start\n* 07/02/10 Reimplement the account list view so the appearance is correct for certain look and feels\n* 06/27/10 Add Substance Look and Feel to the main distribution\n\n== Release 2.3.5\n* 05/20/10 Removed percent gains and unrealized gains from portfolio report because they cannot\n         be accurately calculated\n* 05/08/10 Added Czech localization (Patch #2981896 & 2991446, Luboš Hilgert)\n* 05/08/10 Update Portuguese localization (Patch #2996097, Marco A L Barbosa)\n* 04/04/10 Do not allow the portfolio report to run if there are not any investment accounts present.\n* 04/03/10 Fix typos (Patch #2981190, Nathan McCrina)\n* 03/27/10 Prevent duplicate transaction dialog from resizing too small\n* 03/17/10 Fix typos (Patch #2971980, Adrian A)\n* 03/14/10 Portfolio cost basis was not being calculated correctly\n* 03/14/10 The market value of investment accounts was not reported consistently (Bug #2822512)\n* 03/13/10 Add a simple chart to the Security History dialog\n* 03/10/10 Security price Table was sorting alphabetically instead of numerically (Bug #2940278)\n* 03/09/10 Report unrealized gains correctly in the portfolio report.\n* 03/06/10 Cleaned up internal exchange rate API.\n* 02/24/10 Add context sensitive help capability.\n* 02/24/10 Income tab names were reversed when using accounting terms.\n\n== Release 2.3.4\n* 02/21/10 Expanded help content\n* 02/03/10 Add Ukrainian translation (Vitaliy Aksyonov)\n* 01/20/10 Update to latest JGoodies Forms and Looks to improve layout on OSX and L&F issues on Windows 7\n* 01/19/10 Improve report name consistency for Report/Exports (Patch #2935268, Peter Vida)\n* 01/19/10 Reorganize the Profit Loss Text report into the Report/Exports menu (Patch #2935208, Peter Vida)\n* 01/19/10 Use the scale value specified for Securities in the transaction register table (Peter Vida)\n* 01/18/10 Add cost basis columns to the portfolio report\n* 01/18/10 Add options to the Running and End-of-Month account chart reports to filter placeholder\n         and locked accounts (Patch #2931574, Peter Vida)\n* 01/17/10 XML file corruption could occur for fast parallel jGnash starts (Bug #2929425)\n* 01/17/10 Improved detection of correct OFX encoding when importing (Bug #2929581)\n* 01/16/10 Date selection field was no always displayed correctly (Bug #2931561, Peter Vida)\n* 01/15/10 Fix distribution build so it works on all platforms (Bug #2929859)\n* 01/10/10 Add filtering capability to the account register report (Pranay Kumar)\n* 01/10/10 Allow double clicking a date in the dialog to automatically select and close (Patch #2929289, Peter Vida)\n* 01/10/10 Exchange rates not saved to XML files. (Bug# 2928985, Peter Vida)\n* 01/01/10 Improper amount of cash is transferred from e.g. a bank account to an investment\n\t\t account when more than one fee is assigned to the sell share transaction. (Bug #2924554, Peter Vida)\n* 12/26/09 Fixed a formatting problem affecting the Portfolio Report\n* 12/26/09 Style the report footer text\n* 12/26/09 Update to DynamicJasper 3.0.14\n\n== Release 2.3.3\n* 12/25/09 Reconcile columns were not labeled correctly in the dialog (Bug #2902064)\n* 12/24/09 The latest memorized transaction would not always be recalled\n* 12/17/09 The remote sever now performs periodic XML backups for long running periods if changes have been made\n* 12/09/09 The Profit and Loss Text report was not including the start date as part of the reported balance (Bug #2909000)\n* 12/07/09 Changes made to support operation as a webstart application (Patch #2908944)\n* 11/09/09 Improve formatting of Quantities in the portfolio report (Bug #2892985)\n* 11/08/09 Disable multiple selection of Reminders (Bug #2894147)\n* 11/07/09 Exchange rate of modified transactions was being set to the current rate instead of the prior rate (Pranay Kumar)\n* 11/06/09 Improve UI layout for small screens (netbooks)\n* 11/03/09 Correctly show modifications to currencies without a restart\n* 11/03/09 File import actions should be enabled only if a file is open (Bugs #2890420, #2890422, #2890426)\n* 11/03/09 Update to SwingX 1.6\n\n== Release 2.3.2\n11/02/09 Reports with totals were broken in the 2.3.1 release (Bug #2890310)\n\n== Release 2.3.1\n* 10/30/09 Reports would hang if certain characters were in currency prefix or suffixes (Bug #2884085)\n* 10/23/09 Transaction tab names were reversed when using accounting terms for credit and liability accounts (Bug #2770638)\n* 10/19/09 Reminders with no last date would default to current date when using the XML file format (Bug #2860259)\n* 10/18/09 Update to latest JGoodies look and feel\n* 10/18/09 Use a temporary swap file when generating large reports\n* 10/18/09 Add a group label to the reports to help improve readability\n* 10/15/09 Update to latest DynamicJasper and JasperReports dependencies\n* 10/15/09 Updated German translation (Adrian Gygax)\n* 09/23/09 Fix for Bug #2863303, Improve UI behavior for duplicate transaction behavior (L2K)\n* 07/31/09 Add Yahoo Australia as Quote Source (Rob Hills)\n* 07/09/09 Lazily create the help broker and fail gracefully if an exception occurs instead of\n         preventing the application from starting.\n* 07/07/09 Show the sum of the selected transactions in the register using a tooltip\n* 07/07/09 Liability register was missing the Jump button\n\n== Release 2.3.0\n* 06/26/09 Detect and correct accounts with self parenting\n* 06/20/09 Prevent a user from assigning an account's parent as itself.\n* 06/18/09 Begin migration to MigLayout to replace Forms Layout\n* 06/07/09 Use JXColorSelectionButton to select register colors.\n* 06/05/09 Add network activity indicator when updating security prices and exchange rates in the background.\n* 06/04/09 Update to JasperReports 3.1.4\n* 06/04/09 Add ellipsis symbol to truncated text in reports\n* 06/04/09 Update to DynamicJasper 3.0.6\n* 06/03/09 Correctly handle file encoding of OFX V1 files.\n* 06/01/09 Add a new option to automatically select text when a field receives focus\n* 05/31/09 New report to show income and expense by payee (Pranay Kumar)\n* 05/29/09 Updated Portuguese translation (Pietro Augusto)\n* 05/25/09 Improved handling of validation errors\n* 05/04/09 Integrate the SwingX libraries for improved usability\n* 04/22/09 Fix for Bug #2500229, Display a warning if a Security is not selected when creating an\n         investment transaction.\n* 04/22/09 Correctly handle an attempt to open a zero length file.\n* 04/20/09 Fix for Bug #2734778, Default currency was not accessible immediately after creating a new XML file.\n* 04/10/09 Add an escape key listener to most all dialogs and add additional bounds listening to dialogs that\n         did not already have it.\n\n== Release 2.2.0\n* 03/31/09 Correct identification of OFX 2.0 files that are now starting to show up in the wild.\n* 03/26/09 Fixed report of multiple currencies for the Monthly and End-of-Month account balance charts.\n* 03/26/09 Switched to DocBook for creating content for the JavaHelp system.\n* 03/26/09 Add menu commands to perform background updates on security prices and exchange rates.\n* 03/25/09 Fix for bug #2690988, poor form layout behavior for recurring entry creation in OSX.\n* 03/25/09 Various updates to the Spanish translation (Marcelo Abeldaño).\n* 03/25/09 Transaction reconcile was not occurring per the selected options.\n* 03/25/09 Reconciled state of the opposite side of a transaction was not preserved when modifying.\n* 03/24/09 Fix for bug #2691568 (Andrey Bondarenko).\n* 03/07/09 Much improved account tree UI behavior when security prices change.\n* 03/05/09 Remove unused fields from the Create/Modify Security Dialog.\n* 03/05/09 Improve amortization UI behavior.\n* 03/04/09 Reporting has been reworked.  Report preferences are persistent; Font size is configurable;\n         CSV export has been improved; Consistent appearance for all reports; Now uses Jasper and\n         DynamicJasper report APIs.\n* 02/15/09 Render investment quantities with a fixed decimal to improve appearance.\n\n== Release 2.1.0\n* 02/01/09 Fixed issues with multiple network clients not communicating with each other.\n* 01/14/09 Fixed a problem with duplicate default currencies when creating a new default account set.\n* 01/12/09 Investment account balance was not calculated correctly if the last transaction was a dividend\n         and a security price for same date or after was not established.\n* 01/04/09 Added an integrated help system.\n* 12/30/08 Added -portable command line options to save jGnash preferences to an external location for\n         users who want to run jGnash from a USB drive.\n\n== Release 2.0.3\n* 12/30/08 Checks would print with test border.\n* 12/30/08 Feature Request #2474667, If an invalid file extension is provided during File |  Save As,\n         default to the db4o file type and extension.\n* 12/30/08 Fix for Bug #2474820, Performing File | Save As over the current file would result in an\n         empty file and loss of data.\n* 12/30/08 Update to XStream 1.3.1.  Update should improve XML performance.\n* 12/30/08 Fix new file account structure and import regression.\n* 12/30/08 Patch #2477090, MT940 import fix from Miroslav Holubec.\n* 12/14/08 Add a shutdown option to automatically control the number of backup files.\n* 12/05/08 The automatic Security price download would not work correctly if more than two Securities were configured\n         with no download source.\n* 12/04/08 jGnash can now import Ofx version 1 and 2 credit card account files.\n* 12/03/08 jGnash can now import Ofx version 1 and 2 bank account files.\n* 12/03/08 Fix problem with null account numbers\n\n== Release 2.0.2\n* 11/28/08 Set the default selected account for buy and sell transactions to the base investment account.\n* 11/28/08 Fixed an incorrect warning to the console when modifying and reinvested dividend transaction.\n* 11/28/08 Improved the appearance of the investment transaction entry panels when using the Nimbus look and feel.\n* 11/26/08 Investment account balances were not always reflecting the latest security price.\n* 11/23/08 Disable db4o defragment.  The defragment function is not stable and could cause corruption.\n* 11/23/08 Fix for Bug #2334048, Available Securities dialog was pushing the parent frame to the back.\n* 11/23/08 Fix for Bug #2332586, Modifying an investment transaction from a bank account register was not working.\n* 11/23/08 Fix for Bug #2332540, Loss of focus on an empty numeric field in OSX was throwing an exception. (Fix from Petey)\n* 11/23/08 Internal code cleanup\n* 11/18/08 Dropped Beanshell support because it is no longer supported and does not work well with OSX\n* 11/17/08 Converted the MonthBalanceCSV text report from a Beanshell script to a compiled report.\n* 11/16/08 Converted the ProfitLoss text report from a Beanshell script to a compiled report.\n\n== Release 2.0.1\n* 11/16/08 Update to the latest Pentaho reporting jars.\n* 11/15/08 Prevent the removal of a currency assigned to a security node.\n* 11/10/08 Currency exchange rate was not factored in for investment transaction reconciliation.\n* 11/10/08 Extend default security / exchange download to 30 seconds.  It was 10 seconds.\n* 11/09/08 Fix for Bug #2246569, Date dialog was pushing the parent dialog to the back\n* 11/09/08 Fix for Bug #2222143, Multiple RootAccounts were being created and making import\n         look like it failed.\n\n== Release 2.0.0\n* 11/02/08 The reconciled market balance was not factoring in the exchange rate of currencies\n* 11/02/08 Update to latest JGoodies Looks\n* 10/29/08 Improve appearance of the date selector for modern look and feels (Nimbus and JGoodies)\n* 10/27/08 Fix problem with Reminder modification resulting in a duplicate when\n         using the XML file format\n* 10/17/08 Reconciliation from transaction forms was not working correctly\n* 10/17/08 Automatic reconciliation of income and expense accounts was not working correctly.\n* 10/17/08 Transfer panel was missing the reconcile button\n* 10/16/08 Recurring transaction reminders were not working unless a file was reloaded\n         without UI restart\n* 10/12/08 Fix Portfolio report summary row value\n* 10/10/08 Updated Spanish translation (Marcelo Abeldaño)\n\n== Release 2.0.0-RC4\n* 10/05/08 Typing a 'T' or 't' inside a date field changes it to the current date.\n* 10/05/08 Prevent an exception from occurring if the overall length of a date field is\n         shortened when a shortcut key is used.\n* 10/05/08 Update to the latest JGoodies Forms and Looks jars.\n* 10/05/08 Fix problem with lost views when UI is restarted because of look and feel update\n* 10/04/08 The enabled state of the recurring transaction panel was not correct\n* 10/04/08 Fix the UI layout for the Account Register and Portfolio Reports\n* 10/04/08 Remove unused jar dependency\n\n== Release 2.0.0-RC3\n* 10/01/08 Yahoo UK has reverted to the security symbol instead of the ISIN number for\n         downloading data\n* 10/01/08 Fix for Bug #1991337.  The portfolio report should use the account currency\n         instead of the default currency, and it was not factoring in the exchange rate\n         for securities with different reported currencies.\n* 10/01/08 Change how UI elements are handled when a file is loaded and unload.  This\n         circumvents Java Bug #6472844 which was causing a memory leak.\n* 09/25/08 Yahoo security download info occasionally contains extra white space.  Protect\n         against a NumberFormatException when parsing\n* 09/23/08 Prevent incorrect moving of an account\n* 09/23/08 Update to latest JFreeChart jar\n* 09/23/08 Update to latest db40 6.4 jar\n* 09/22/08 Fix for Bug #2080742.  The direction of the currency conversion was not correct\n* 09/21/08 Correctly set the enabled state of the Reports menu when a file is not loaded\n* 09/20/08 Prevent the import of a MT940 file if a jGnash file is not loaded\n* 09/20/08 Fix for Bug #2098347.  Prevent the import of an OFX file if a jGnash file is not loaded\n* 09/19/08 Fix the enabled state of the reminder panel buttons and prevent an NPE if\n         a file is not loaded.\n* 09/17/08 Fix the investment account reconciliation process\n* 09/09/08 Fix the reported reconciled amount for investment accounts\n* 08/27/08 Fix for Bug #2068074. Reminder modifications were not handled correctly\n* 08/20/08 Localization fixes\n* 08/18/08 Update to latest Pentaho reporting jar\n\n== Release 2.0.0-RC2\n* 08/18/08 Add sort capability to currency exchange table\n* 08/17/08 Add Copy to Clipboard button to Console and Exception dialogs\n* 08/17/08 HTTP connections were left open when downloading security history\n* 08/17/08 Change sort order of the accounts for reports\n* 08/17/08 Restart the UI when the L&F is changed to prevent Exceptions\n* 08/15/08 Spanish translation fixes (Marcelo Abeldaño)\n* 08/14/08 Correctly handle a filename passed by Windows if associated with jGnash\n* 08/14/08 Fix NPE in recurring transactions\n* 08/13/08 Fixed 1.x import and behavior of BuyX and SellX transactions\n* 07/31/08 Change EDT check to used a command line option\n* 07/30/08 The reconciled balance was not always rendered in the correct color\n* 07/29/08 Use the default sort icons for the table header in the transaction register\n* 07/28/08 Fix the appearance of the table header in the transaction register for newer\n         look and feels\n* 07/28/08 The duplicate function for transactions was not working for split transactions\n* 07/27/08 Allow sorting of the security history table\n* 07/27/08 Yahoo UK parser was not using the ISIN number\n* 07/27/08 Fix more EDT issues\n\n== Release 2.0.0-RC1\n* 07/27/08 The lookup mechanism for default account sets when creating a new file did not work\n         when jGnash was run from a jar or exe.\n* 07/26/08 Currency Exchange history dialog was not always showing the correct conversion direction\n* 07/23/08 Fix some initial display issues with SecurityHighLowChart\n* 07/22/08 The XML storage container would not remove objects as expected\n* 07/22/08 Add UI option to export timestamped and compressed file on exit\n* 07/21/08 Update to JFreeChart 1.0.10.  Fixes some quirks with the income/expense pie chart\n* 07/21/08 Create all UI elements on the EDT\n* 07/19/08 Fix a NPE if the RootAccount AccountGroup is requested\n* 07/19/08 Fix a potential problem with stray account properties being left in the object database\n         upon account removal\n* 07/18/08 Fix Profit and Loss text report and Monthly Balance export scripts\n* 07/16/08 Save a time-stamped and compressed file on exit if enabled\n* 07/14/08 Implement full Save As functionality.  It is now possible to switch between file formats.\n\n== Release 2.0.0 - Beta 3\n* 07/12/08 Lock XML file at OS level to prevent overwrite from multiple instances of jGnash\n* 07/07/08 New icons to update UI appearance\n* 07/06/08 Add a reconciled balance column to the accounts overview\n* 07/06/08 XML Datastore is now working\n* 06/30/08 Reinstate the 1.x status bar\n* 06/29/08 Fixed a validation problem that prevented 0 scale currencies from being added to the database\n* 06/28/08 Enable full support of client / server connection from the command line\n* 06/21/08 AmortizeObject does not have to extend StoredObject\n* 06/21/08 Enable option to load a file from the command line\n* 06/20/08 TransactionEntry does not have to extend StoredObject\n* 06/18/08 Balance Sheet report was not pulling all account types correctly\n* 06/17/08 Fix bad validation code for jGnash 1.x import.  Depends on update release of Java 6.\n\n== Release 2.0.0 - Beta 2\n* 06/16/08 Preselect default transaction form tab based on account type\n* 06/15/08 Dump GnuCash import support\n* 06/15/08 SecurityNode and TransactionEntry db schema change.  db4o does not handle changes to enums well\n* 06/14/08 Add \"Checking\" account type\n* 06/14/08 Account db schema change.  db4o does not handle changes to enums well\n* 06/13/08 If a transaction is dated for the future, italicize the font in the register table\n* 06/13/08 Soft null check Workaround for a weird JVM bug for null assert checks on non-null Strings with international characters.\n* 06/12/08 Autocomplete was occurring when text was being set vs typed causing mysterious changes to fields.\n* 06/11/08 Add missing top level memo for transactions\n* 06/11/08 Fix enabled state of the account combo for split transaction entry\n* 06/09/08 Fix the display of split details for the account register report\n\n== Release 2.0.0 - Beta 1\n* 06/08/08 Reduce XML export file size by 45%\n* 06/06/08 Dumped some unused legacy methods from TransactionEntry and subclasses\n* 06/05/08 Fix transaction generation for basic double entry panel\n* 06/05/08 Do not allow the currency of an account to be changed to it already contains transactions.\n* 06/04/08 Overhauled the register tree panel code to fix column resize behavior and fix some bugs\n* 06/03/08 Fix last known data corruption bug (Was not cloning TransactionEntries in the FeesPanel)\n* 05/31/08 Reworked UI and API for reinvested transactions\n* 05/26/08 Use new exchange rate UI for bank and transfer transactions\n* 05/23/08 Remove duplicate code in TransactionDAO\n* 05/22/08 New API and UI for handling capital gains and loss\n* 05/22/08 Use java collections for storage instead of manually controlled arrays\n* 05/08/08 Disable web update in Security History Dialog if a download source has not been selected for the security\n* 04/12/08 Save and restore the last active view\n* 04/06/08 Open streams were not being closed\n* 04/06/08 Fixed formatting error in balance sheet and networth reports\n* 03/25/08 Fixed import of jGnash 1.x Dividend transactions\n* 03/20/08 Begin separation of BuyX and SellX transaction forms\n* 03/10/08 Improve fees handling for BuyX transactions\n* 03/05/08 Applied patch #1907963 for improved OFX parsing (Nicolas Bouillon)\n* 03/03/08 Improved TransactionDialog\n* 03/03/08 Fix divide by zero bug #1906150\n* 03/01/08 Fix localization bug #1903842\n* 02/29/08 Place nice with upcoming Nimbus look and feel\n* 02/29/08 Update to jGoodies 1.2.0\n* 02/27/08 Improve Next # action for transaction numbers Bug #1902455\n* 02/21/08 Support for multiple security quote sources (Yahoo! and Yahoo! UK)\n* 02/21/08 Improved OFX header parsing\n* 01/31/08 Merge mt940 import support\n* 01/30/08 Use of accounting terms were not correct in all cases.\n* 01/22/08 Fixed handling for split and merge transactions in the portfolio report.\n* 01/01/08 Reworked Dividend transactions and UI to support true double entry.\n* 01/01/08 Use TimingFramework instead of jGoodies animations.\n* 12/27/07 A button was added to the investment register to allow selection of available securities.\n* 12/26/07 Improve generated payee of investment transactions.\n\n== Release 2.0.0 - Alpha 3\n* 12/26/07 Added Working OFX import for savings and checking accounts.\n* 12/17/07 Improved new account wizard so user can add default account structures\n* 12/10/07 Added import and export of the account tree\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.8.2-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# suppress inspection \"UnusedProperty\" for whole file\n\n# https://github.com/openjfx/javafx-gradle-plugin/releases\njavafxPluginVersion=0.0.9\n\n# https://github.com/ben-manes/gradle-versions-plugin/releases\nversionsPluginVersion=0.36.0\n\n# https://mvnrepository.com/artifact/org.openjfx/javafx\njavaFXVersion=15.0.1\n\n# https://github.com/TestFX/TestFX/releases\ntestFxVersion=4.0.16-alpha\n\n# https://github.com/TestFX/Monocle/releases\nmonocleVersion=jdk-12.0.1+2\n\n# https://plugins.gradle.org/plugin/edu.sc.seis.macAppBundle\nmacAppBundleVersion=2.3.0\n\n# https://junit.org/junit5/\njunitVersion=5.7.1\n\n# https://github.com/glytching/junit-extensions\njunitExtensionsVersion=2.4.0\n\n# https://github.com/awaitility/awaitility\nawaitilityVersion=4.0.3\n\n# https://commons.apache.org/proper/commons-lang/\ncommonsLangVersion=3.11\n\n# https://commons.apache.org/proper/commons-math/\ncommonsMathVersion=3.6.1\n\n# https://commons.apache.org/proper/commons-csv/\ncommonsCsvVersion=1.8\n\n#https://commons.apache.org/proper/commons-collections/\ncommonsCollectionsVersion=4.4\n\n# https://commons.apache.org/proper/commons-text/\ncommonsTextVersion=1.9\n\n# https://github.com/remkop/picocli\npicocliVersion=4.6.1\n\n# https://poi.apache.org/\napachePoiVersion=5.0.0\n\n# http://www.h2database.com/html/main.html\nh2Version=1.4.200\n\n# http://hsqldb.org/\nhsqldbVersion=2.5.1\n\n# https://github.com/x-stream/xstream\nxstreamVersion=1.4.15\n\n# http://hibernate.org/orm/\nhibernateVersion=5.4.28.Final\n\n# https://github.com/brettwooldridge/HikariCP\nhikariVersion=4.0.2\n\n# https://pdfbox.apache.org/\npdfBoxVersion=2.0.22\n\n# https://netty.io/index.html\nnettyVersion=4.1.59.Final\n\n# https://www.slf4j.org/news.html\nslf4jVersion = 1.8.0-beta4\n\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "jGnash",
    "content": "#!/usr/bin/env sh\n\ncwd=$(dirname \"$0\")\n\"$cwd\"/bin/bootloader \"$@\"\n\nif [ $? -eq 100 ]; then #relaunch automatically if return value is 100\n  \"$cwd\"/bin/bootloader \"$@\"\nfi\n\nexit 0\n"
  },
  {
    "path": "jgnash-bayes/build.gradle.kts",
    "content": "description = \"jGnash Bayes\"\n\nval moduleName = \"jgnash.bayes\"\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}"
  },
  {
    "path": "jgnash-bayes/src/main/java/jgnash/bayes/BayesClassifier.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.bayes;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.regex.Pattern;\n\n/**\n * Naive Bayes BayesClassifier.\n * Modeled after classifier presented in \"Programming Collective Intelligence\" by Toby Segaran\n *\n * @param <E> the type of mapped value\n * \n * @author Craig Cavanaugh\n */\npublic class BayesClassifier<E> {\n\n    /**\n     * Default class if not determinate\n     */\n    private final E defaultClass;\n\n    private static final double ASSUMED_PROBABILITY = 0.5;\n    private static final double THRESHOLD = 1.0;\n    private static final double WEIGHT = 1.0;\n    private static final String WHITE_SPACE_REGEX = \"[,\\\\s]+\";\n    // private final static String NUMBERS_REGEX = \"(?>-?\\\\d+(?:[\\\\./]\\\\d+)?)\";\n\n    private final Map<String, Map<E, Integer>> featureCounter = new HashMap<>();\n    private final Map<E, Integer> classCounter = new HashMap<>();\n    private final Pattern whiteSpacePattern;\n\n    /**\n     * Constructor\n     * \n     * @param defaultClass the mapped type\n     */\n    public BayesClassifier(final E defaultClass) {\n        this.defaultClass = defaultClass;\n        whiteSpacePattern = Pattern.compile(WHITE_SPACE_REGEX);\n    }\n\n    private void incrementFeature(final String feature, final E classification) {\n        Map<E, Integer> featureMap = featureCounter.get(feature);\n\n        if (featureMap == null) {\n            featureMap = new HashMap<>();\n            featureMap.put(classification, 1);\n            featureCounter.put(feature, featureMap);\n            return;\n        }\n\n        Integer count = featureMap.get(classification);\n        count = (count == null) ? 1 : count + 1;\n        featureMap.put(classification, count);\n    }\n\n    /**\n     * Support method to increment a classification\n     *\n     * @param classification classification to increment\n     */\n    private void incrementClass(final E classification) {\n        Integer count = classCounter.get(classification);\n        count = (count == null) ? 1 : count + 1;\n        classCounter.put(classification, count);\n    }\n\n    private int getClassCount(final E classification) {\n        Integer count = classCounter.get(classification);\n\n        return (count != null) ? count : 0;\n    }\n\n    /**\n     * Gets the number of times the feature as occurred in the classification\n     *\n     * @param feature        feature to count\n     * @param classification class\n     * @return occurrence count\n     */\n    private int getFeatureCount(final String feature, final E classification) {\n        Map<E, Integer> featureMap = featureCounter.get(feature);\n\n        if (featureMap == null) {\n            return 0;\n        }\n        Integer count = featureMap.get(classification);\n\n        return (count != null) ? count : 0;\n    }\n\n    private int getFeatureCount(final String feature) {\n        Map<E, Integer> featureMap = featureCounter.get(feature);\n        int count = 0;\n\n        if (featureMap == null) {\n            return count;\n        }               \n        \n        for (Entry<E, Integer> entry : featureMap.entrySet()) {\n            count += entry.getValue();\n        }                    \n\n        return count;\n    }\n\n    private double getFeatureProbability(final String feature, final E classification) {\n        int count = getClassCount(classification);\n        return (count == 0) ? 0.0 : (double) getFeatureCount(feature, classification) / count;\n    }\n\n    private double getWeightedProbability(final String feature, final E classification) {\n        double probability = getFeatureProbability(feature, classification);\n        int totals = getFeatureCount(feature);\n        return (WEIGHT * ASSUMED_PROBABILITY + totals * probability) / (WEIGHT + totals);\n    }\n\n    private double getClassProbability(final String item, final E classification) {\n        double probability = 1;\n\n        for (final String feature : whiteSpacePattern.split(item)) {\n            probability *= getWeightedProbability(feature, classification);\n        }\n\n        return (double) classCounter.get(classification) / classCounter.size() * probability;\n    }\n\n    private void train(final Collection<String> features, final E classification) {\n        for (String feature : features) {\n            incrementFeature(feature, classification);\n        }\n        incrementClass(classification);\n    }\n\n    /**\n     * Trains the classifier\n     * \n     * @param item training string\n     * @param classification object being classified\n     */\n    public void train(final String item, final E classification) {\n        train(Arrays.asList(whiteSpacePattern.split(item.toLowerCase(Locale.getDefault()))), classification);\n    }\n\n    /**\n     * Returns the best probabilistic match \n     * \n     * @param item String data to match\n     * @return best possible match\n     */\n    public E classify(final String item) {\n        E bestClass = defaultClass;\n        double classProb;\n        double bestProbability;\n        double max = 0;\n\n        Map<E, Double> probabilities = new HashMap<>();\n\n        // find the category with the highest probability\n        for (final E classification : classCounter.keySet()) {\n            classProb = getClassProbability(item.toLowerCase(Locale.getDefault()), classification);\n            probabilities.put(classification, classProb);\n            if (classProb > max) {\n                max = classProb;\n                bestClass = classification;\n            }\n        }\n                       \n        // make sure the probability exceeds\n        for (final Entry<E, Double> entry : probabilities.entrySet()) {\n            if (!entry.getKey().equals(bestClass)) {\n                classProb = entry.getValue();\n                bestProbability = probabilities.get(bestClass);\n                \n                if (classProb * THRESHOLD >= bestProbability) {\n                    return defaultClass;\n                }\n            }\n        }       \n\n        return bestClass;\n    }\n}"
  },
  {
    "path": "jgnash-convert/build.gradle.kts",
    "content": "description = \"jGnash Convert\"\n\nval moduleName = \"jgnash.convert\"\nval commonsCsvVersion: String by project\n\nplugins {\n    `java-library`\n}\n\ndependencies {\n    implementation(project(\":jgnash-resources\"))\n    implementation(project(\":jgnash-core\"))\n    implementation(project(\":jgnash-bayes\"))\n\n    implementation(\"org.apache.commons:commons-csv:$commonsCsvVersion\")\n}\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/common/OfxTags.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.common;\n\n/**\n * Known OFX tags\n *\n * @author Craig Cavanaugh\n * @author Nicolas Bouillon\n */\npublic interface OfxTags {\n\n    /**\n     * Account ID\n     */\n    String ACCTID = \"ACCTID\";\n\n    /**\n     * Account Type\n     */\n    String ACCTTYPE = \"ACCTTYPE\";\n\n    /**\n     * Available balance\n     */\n    String AVAILBAL = \"AVAILBAL\";\n\n    /**\n     * Balance Amount\n     */\n    String BALAMT = \"BALAMT\";\n\n    /**\n     * Bank Account info\n     */\n    String BANKACCTFROM = \"BANKACCTFROM\";\n\n    /**\n     * Bank Account info for a transfer\n     */\n    String BANKACCTTO = \"BANKACCTTO\";\n\n    /**\n     * CASH Tag\n     */\n    String CASH = \"CASH\";\n\n    /**\n     * Credit Card info\n     */\n    String CCACCTFROM = \"CCACCTFROM\";\n\n    /**\n     * Credit Account info for a transfer\n     */\n    String CCACCTTO = \"CCACCTTO\";\n\n    /**\n     * Investment Account info\n     */\n    String INVACCTFROM = \"INVACCTFROM\";\n\n    /**\n     * Investment Account info for a transfer\n     */\n    String INVACCTTO = \"INVACCTTO\";\n\n    /**\n     * Bank ID\n     */\n    String BANKID = \"BANKID\";\n\n    /**\n     * Stock purchase\n     */\n    String BUYSTOCK = \"BUYSTOCK\";\n\n    /**\n     * Purchase type, normally \"BUY\"\n     */\n    String BUYTYPE = \"BUYTYPE\";\n\n    /**\n     * Buy other security type\n     */\n    String BUYOTHER = \"BUYOTHER\";\n\n    /**\n     * Buy mutual fund\n     */\n    String BUYMF = \"BUYMF\";\n\n    /**\n     * Sub-account that security or cash is being transferred to: CASH, MARGIN, SHORT, OTHER\n     */\n    String SUBACCTTO = \"SUBACCTTO\";\n\n    /**\n     * Sub-account that security or cash is being transferred from: CASH, MARGIN, SHORT, OTHER\n     */\n    String SUBACCTFROM = \"SUBACCTFROM\";\n\n    /**\n     * Investment account position list\n     */\n    String INVPOSLIST = \"INVPOSLIST\";\n\n    /**\n     * Investment account balance information\n     */\n    String INVBAL = \"INVBAL\";\n\n    /**\n     * Open investment transaction orders list\n     */\n    String INVOOLIST = \"INVOOLIST\";\n\n    /**\n     * Branch identifier. May be required for some non-US banks\n     */\n    String BRANCHID = \"BRANCHID\";\n\n    /**\n     * Bank transaction list\n     */\n    String BANKTRANLIST = \"BANKTRANLIST\";\n\n    String INVTRANLIST = \"INVTRANLIST\";\n\n    String INVTRAN = \"INVTRAN\";\n\n    String INVBUY = \"INVBUY\";\n\n    String INVSELL = \"INVSELL\";\n\n    String BROKERID = \"BROKERID\";\n\n    /**\n     * Check number\n     */\n    String CHECKNUM = \"CHECKNUM\";\n\n    String CODE = \"CODE\";\n\n    String CURDEF = \"CURDEF\";\n\n    String CURRENCY = \"CURRENCY\";\n\n    /**\n     * Checking account type\n     * @see #ACCTTYPE\n     */\n    String CHECKING = \"CHECKING\";\n\n    /**\n     * Credit line account type\n     * @see #ACCTTYPE\n     */\n    String CREDITLINE = \"CREDITLINE\";\n\n    /**\n     * Money market account type\n     * @see #ACCTTYPE\n     */\n    String MONEYMRKT = \"MONEYMRKT\";\n\n    /**\n     * Savings account type\n     * @see #ACCTTYPE\n     */\n    String SAVINGS = \"SAVINGS\";\n\n    /**\n     * Credit transaction\n     * @see #TRNTYPE\n     */\n    String CREDIT = \"CREDIT\";\n\n    /**\n     * Debit transaction\n     * @see #TRNTYPE\n     */\n    String DEBIT = \"DEBIT\";\n\n    /**\n     * Date of balance\n     * @see #LEDGERBAL\n     * @see #AVAILBAL\n     */\n    String DTASOF = \"DTASOF\";\n\n    /**\n     * End date of transaction list\n     */\n    String DTEND = \"DTEND\";\n\n    /**\n     * Date posted\n     */\n    String DTPOSTED = \"DTPOSTED\";\n\n    /**\n     * Date user initiated transaction\n     */\n    String DTUSER = \"DTUSER\";\n\n    String DTSERVER = \"DTSERVER\";\n\n    /**\n     * Start date of transaction list\n     */\n    String DTSTART = \"DTSTART\";\n\n    String DTTRADE = \"DTTRADE\";\n\n    String DTSETTLE = \"DTSETTLE\";\n\n    /**\n     * Fees applied to trade, amount\n     */\n    String FEES = \"FEES\";\n\n    String FI = \"FI\";\n\n    String FID = \"FID\";\n\n    /**\n     * Financial Institution transaction id\n     */\n    String FITID = \"FITID\";\n\n\n    String LANGUAGE = \"LANGUAGE\";\n\n    /**\n     * Account balance\n     */\n    String LEDGERBAL = \"LEDGERBAL\";\n\n    /**\n     * Transaction memo\n     */\n    String MEMO = \"MEMO\";\n\n    /**\n     * Chase bank mucking up the OFX standard\n     */\n    String CATEGORY = \"CATEGORY\";\n\n    String MESSAGE = \"MESSAGE\";\n\n    /**\n     * Name of payee or transaction description, may be used exclusive of {@code PAYEE}\n     * @see #PAYEE\n     */\n    String NAME = \"NAME\";\n\n    String OFX = \"OFX\";\n\n    String ORG = \"ORG\";\n\n    String ORIGCURRENCY = \"ORIGCURRENCY\";\n\n    /**\n     * Name of payee, may be used exclusive of {@code NAME}\n     * @see #NAME\n     */\n    String PAYEE = \"PAYEE\";\n\n    String PAYEEID = \"PAYEEID\";\n\n    /**\n     * Indicates an amount withheld due to a penalty. Amount\n     */\n    String PENALTY = \"PENALTY\";\n\n    String REFNUM = \"REFNUM\";\n\n    String SEVERITY = \"SEVERITY\";\n\n    String SECID = \"SECID\";\n\n    /**\n     * Accounting SIC code\n     */\n    String SIC = \"SIC\";\n\n    /**\n     * Sign-on Message Set Aggregate\n     */\n    String SIGNONMSGSRSV1 = \"SIGNONMSGSRSV1\";\n\n    String SONRS = \"SONRS\";\n\n    String STATUS = \"STATUS\";\n\n    /**\n     * Bank statement response aggregate\n     */\n    String STMTRS = \"STMTRS\";\n\n    /**\n     * Investment account bank transaction\n     */\n    String INVBANKTRAN = \"INVBANKTRAN\";\n\n    /**\n     * Credit Card statement response aggregate\n     */\n    String CCSTMTRS = \"CCSTMTRS\";\n\n    /**\n     * Investment statement response aggregate\n     */\n    String INVSTMTRS = \"INVSTMTRS\";\n\n    /**\n     * Bank Transaction\n     */\n    String STMTTRN = \"STMTTRN\";\n\n    String STMTTRNRS = \"STMTTRNRS\";\n\n    String SUBACCTSEC = \"SUBACCTSEC\";\n\n    /**\n     * Where did the money for the transaction come from or go to? CASH, MARGIN, SHORT, OTHER\n     */\n    String SUBACCTFUND = \"SUBACCTFUND\";\n\n    /**\n     * Sell a mutual fund\n     */\n    String SELLMF = \"SELLMF\";\n\n    /**\n     * Sell other type of security\n     */\n    String SELLOTHER = \"SELLOTHER\";\n\n    /**\n     * Sell a stock\n     */\n    String SELLSTOCK = \"SELLSTOCK\";\n\n    String SELLTYPE = \"SELLTYPE\";\n\n    String CCSTMTTRNRS = \"CCSTMTTRNRS\";\n\n    String INVSTMTTRNRS = \"INVSTMTTRNRS\";\n\n    String REINVEST = \"REINVEST\";\n\n    String INCOME = \"INCOME\";\n\n    String INCOMETYPE = \"INCOMETYPE\";\n\n    /**\n     * 401k loan id\n     */\n    String LOANID = \"LOANID\";\n\n    /**\n     * 401k loan principal\n     */\n    String LOANPRINCIPAL = \"LOANPRINCIPAL\";\n\n    /**\n     * 401k loan interest\n     */\n    String LOANINTEREST = \"LOANINTEREST\";\n\n    /**\n     * Must be one of the following: PRETAX, AFTERTAX, MATCH, PROFITSHARING, ROLLOVER, OTHERVEST, OTHERNONVEST\n     */\n    String INV401KSOURCE = \"INV401KSOURCE\";\n\n    /**\n     * For 401(k)accounts, date the funds for this transaction was obtained via payroll deduction, datetime\n     */\n    String DTPAYROLL = \"DTPAYROLL\";\n\n    /**\n     * For 401(k) accounts, indicates that this Buy was made with a prior year contribution. Boolean\n     */\n    String PRIORYEARCONTRIB = \"PRIORYEARCONTRIB\";\n\n    /**\n     * For 401(k) accounts, account balance aggregate\n     */\n    String INV401KBAL = \"INV401KBAL\";\n\n    /**\n     * For 401(k) accounts, account information aggregate\n     */\n    String INV401K = \"INV401K\";\n\n    /**\n     * Tax exempt status of an investment transactions\n     */\n    String TAXEXEMPT = \"TAXEXEMPT\";\n\n    /**\n     * Transaction amount\n     */\n    String TRNAMT = \"TRNAMT\";\n\n    /**\n     * Transaction type\n     */\n    String TRNTYPE = \"TRNTYPE\";\n\n    /**\n     * Client Assigned Globally Unique Transaction ID\n     */\n    String TRNUID = \"TRNUID\";\n\n    /**\n     * Total of the investment transaction (unit * unit price + commission)\n     */\n    String TOTAL = \"TOTAL\";\n\n    //String USERKEY = \"USERKEY\";\n\n    String UNIQUEID = \"UNIQUEID\";\n\n    String UNIQUEIDTYPE = \"UNIQUEIDTYPE\";\n\n    String UNITS = \"UNITS\";\n\n    String UNITPRICE = \"UNITPRICE\";\n\n    String COMMISSION = \"COMMISSION\";\n\n    /**\n     * Bank Message Set Aggregate\n     */\n    String BANKMSGSRSV1 = \"BANKMSGSRSV1\";\n\n    String CREDITCARDMSGSRSV1 = \"CREDITCARDMSGSRSV1\";\n\n    String INVSTMTMSGSRSV1 = \"INVSTMTMSGSRSV1\";\n\n    String SECLISTMSGSRSV1 = \"SECLISTMSGSRSV1\";\n\n\n    /**\n     * Security Info\n     */\n    String STOCKINFO = \"STOCKINFO\";\n\n    /**\n     * Mutual fund information\n     */\n    String MFINFO = \"MFINFO\";\n\n    /**\n     * Security information\n     */\n    String SECINFO = \"SECINFO\";\n\n    /**\n     * Information about an Option\n     */\n    String OPTINFO = \"OPTINFO\";\n\n    /**\n     * Option type\n     */\n    String OPTTYPE = \"OPTTYPE\";\n\n    /**\n     * Strike price\n     */\n    String STRIKEPRICE = \"STRIKEPRICE\";\n\n    /**\n     * ISO-4217 3-letter currency identifier\n     */\n    String CURSYM = \"CURSYM\";\n\n    /**\n     * Ratio of <CURDEF> currency to <CURSYM> currency, in decimal notation, rate\n     */\n    String CURRATE = \"CURRATE\";\n\n    /**\n     * Security name, maximum of 120 characters\n     */\n    String SECNAME = \"SECNAME\";\n\n    String TICKER = \"TICKER\";\n\n    String SECLIST = \"SECLIST\";\n\n    /**\n     * Expiration date for an Option\n     */\n    String DTEXPIRE = \"DTEXPIRE\";\n\n    /**\n     * Number of shares per contract\n     */\n    String SHPERCTRCT = \"SHPERCTRCT\";\n\n    /**\n     * Asset class of the security\n     */\n    String ASSETCLASS = \"ASSETCLASS\";\n\n    /**\n     * Yield of the security\n     */\n    String YIELD = \"YIELD\";\n\n    /**\n     * Internal security identifier for the financial institution\n     */\n    String FIID = \"FIID\";\n\n    /**\n     * Security rating, maximum of 10 characters\n     */\n    String RATING = \"RATING\";\n\n    /**\n     * Intuit mucking up the OFX standard, Bank Id, In signon message\n     */\n    String INTUBID = \"INTU.BID\";\n\n    /**\n     * Intuit mucking up the OFX standard, User Id, In signon message\n     */\n    String INTUUSERID = \"INTU.USERID\";\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/exportantur/csv/CsvExport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.exportantur.csv;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\nimport org.apache.commons.csv.CSVFormat;\nimport org.apache.commons.csv.CSVPrinter;\nimport org.apache.commons.csv.QuoteMode;\n\n/**\n * Primary class for CSV export\n *\n * @author Craig Cavanaugh\n */\npublic class CsvExport {\n    private static final char BYTE_ORDER_MARK = '\\ufeff';\n\n    private static final String SPACE = \" \";\n\n    private static final int INDENT = 4;\n\n    private CsvExport() {\n    }\n\n    public static void exportAccountTree(@NotNull final Engine engine, @NotNull final Path path) {\n        Objects.requireNonNull(engine);\n        Objects.requireNonNull(path);\n\n        // force a correct file extension\n        final String fileName = FileUtils.stripFileExtension(path.toString()) + \".csv\";\n\n        final CSVFormat csvFormat = CSVFormat.EXCEL.withQuoteMode(QuoteMode.ALL);\n\n        try (final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(Files.newOutputStream(Paths.get(fileName)),\n                StandardCharsets.UTF_8);\n             final CSVPrinter writer = new CSVPrinter(new BufferedWriter(outputStreamWriter), csvFormat)) {\n\n            outputStreamWriter.write(BYTE_ORDER_MARK); // write UTF-8 byte order mark to the file for easier imports\n\n            writer.printRecord(ResourceUtils.getString(\"Column.Account\"), ResourceUtils.getString(\"Column.Code\"),\n                    ResourceUtils.getString(\"Column.Entries\"), ResourceUtils.getString(\"Column.Balance\"),\n                    ResourceUtils.getString(\"Column.ReconciledBalance\"), ResourceUtils.getString(\"Column.Currency\"),\n                    ResourceUtils.getString(\"Column.Type\"));\n\n            // Create a list sorted by depth and account code and then name if code is not specified\n            final List<Account> accountList = engine.getAccountList();\n            accountList.sort(Comparators.getAccountByTreePosition(Comparators.getAccountByCode()));\n\n            final CurrencyNode currencyNode = engine.getDefaultCurrency();\n            final LocalDate today = LocalDate.now();\n\n            for (final Account account : accountList) {\n                final String indentedName = SPACE.repeat((account.getDepth() - 1) * INDENT) + account.getName();\n                final String balance = account.getTreeBalance(today, currencyNode).toPlainString();\n                final String reconcileBalance = account.getReconciledTreeBalance().toPlainString();\n\n                writer.printRecord(indentedName, String.valueOf(account.getAccountCode()), String.valueOf(account.getTransactionCount()),\n                        balance, reconcileBalance, account.getCurrencyNode().getSymbol(), account.getAccountType().toString());\n            }\n        } catch (final IOException e) {\n            Logger.getLogger(CsvExport.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    public static void exportAccount(@NotNull final Account account, @NotNull final LocalDate startDate,\n                                     @NotNull final LocalDate endDate, @NotNull final Path path) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(startDate);\n        Objects.requireNonNull(endDate);\n        Objects.requireNonNull(path);\n\n        // force a correct file extension\n        final String fileName = FileUtils.stripFileExtension(path.toString()) + \".csv\";\n\n        final CSVFormat csvFormat = CSVFormat.EXCEL.withQuoteMode(QuoteMode.ALL);\n\n        try (final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(Files.newOutputStream(Paths.get(fileName)),\n                StandardCharsets.UTF_8);\n             final CSVPrinter writer = new CSVPrinter(new BufferedWriter(outputStreamWriter), csvFormat)) {\n\n            outputStreamWriter.write(BYTE_ORDER_MARK); // write UTF-8 byte order mark to the file for easier imports\n\n            writer.printRecord(ResourceUtils.getString(\"Column.Account\"), ResourceUtils.getString(\"Column.Num\"),\n                    ResourceUtils.getString(\"Column.Debit\"), ResourceUtils.getString(\"Column.Credit\"),\n                    ResourceUtils.getString(\"Column.Balance\"), ResourceUtils.getString(\"Column.Date\"),\n                    ResourceUtils.getString(\"Column.Timestamp\"), ResourceUtils.getString(\"Column.Memo\"),\n                    ResourceUtils.getString(\"Column.Payee\"), ResourceUtils.getString(\"Column.Clr\"));\n\n            // write the transactions\n            final List<Transaction> transactions = account.getTransactions(startDate, endDate);\n\n            final DateTimeFormatter dateTimeFormatter = DateUtils.getExcelDateFormatter();\n\n            final DateTimeFormatter timestampFormatter = DateUtils.getExcelTimestampFormatter();\n\n            for (final Transaction transaction : transactions) {\n                final String date = dateTimeFormatter.format(transaction.getLocalDate());\n\n                final String timeStamp = timestampFormatter.format(transaction.getTimestamp());\n\n                final String credit = transaction.getAmount(account).compareTo(BigDecimal.ZERO) < 0 ? \"\"\n                                              : transaction.getAmount(account).abs().toPlainString();\n\n                final String debit = transaction.getAmount(account).compareTo(BigDecimal.ZERO) > 0 ? \"\"\n                                             : transaction.getAmount(account).abs().toPlainString();\n\n                final String balance = account.getBalanceAt(transaction).toPlainString();\n\n                final String reconciled = transaction.getReconciled(account) == ReconciledState.NOT_RECONCILED\n                                                  ? Boolean.FALSE.toString() : Boolean.TRUE.toString();\n\n                writer.printRecord(account.getName(), transaction.getNumber(), debit, credit, balance, date, timeStamp,\n                        transaction.getMemo(), transaction.getPayee(), reconciled);\n            }\n        } catch (final IOException e) {\n            Logger.getLogger(CsvExport.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/exportantur/ofx/OfxExport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.exportantur.ofx;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.math.BigDecimal;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.convert.common.OfxTags;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Primary class for OFX export. The SGML format is used instead of the newer\n * XML to offer the best compatibility with older importers\n *\n * @author Craig Cavanaugh\n */\npublic class OfxExport implements OfxTags {\n\n    private static final String[] OFXHEADER = new String[]{\"OFXHEADER:100\", \"DATA:OFXSGML\", \"VERSION:102\",\n            \"SECURITY:NONE\", \"ENCODING:USASCII\", \"CHARSET:1252\", \"COMPRESSION:NONE\", \"OLDFILEUID:NONE\",\n            \"NEWFILEUID:NONE\"};\n\n    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(\"yyyyMMdd\");\n\n    private final Account account;\n\n    private final LocalDate startDate;\n\n    private final LocalDate endDate;\n\n    private final File file;\n\n    private IndentedPrintWriter indentedWriter;\n\n    private int indentLevel = 0;\n\n    public OfxExport(final Account account, final LocalDate startDate, final LocalDate endDate, final File file) {\n        this.account = account;\n        this.startDate = startDate;\n        this.endDate = endDate;\n        this.file = file;\n    }\n\n    public void exportAccount() {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(startDate);\n        Objects.requireNonNull(endDate);\n        Objects.requireNonNull(file);\n\n        final LocalDate exportDate = LocalDate.now();\n\n        // force a correct file extension\n        final String fileName = FileUtils.stripFileExtension(file.getAbsolutePath()) + \".ofx\";\n\n        try (final IndentedPrintWriter writer = new IndentedPrintWriter(Files.newBufferedWriter(Paths.get(fileName),\n                Charset.forName(\"windows-1252\")))) {\n\n            indentedWriter = writer;\n\n            // write the required header\n            for (String line : OFXHEADER) {\n                writer.println(line, indentLevel);\n            }\n            writer.println();\n\n            // start of data\n            writer.println(wrapOpen(OFX), indentLevel++);\n\n            // write sign-on response\n            writer.println(wrapOpen(SIGNONMSGSRSV1), indentLevel++);\n            writer.println(wrapOpen(SONRS), indentLevel++);\n            writer.println(wrapOpen(STATUS), indentLevel++);\n            writer.println(wrapOpen(CODE) + \"0\", indentLevel);\n            writer.println(wrapOpen(SEVERITY) + \"INFO\", indentLevel);\n            writer.println(wrapClose(STATUS), --indentLevel);\n            writer.println(wrapOpen(DTSERVER) + encodeDate(exportDate), indentLevel);\n            writer.println(wrapOpen(LANGUAGE) + \"ENG\", indentLevel);\n            writer.println(wrapClose(SONRS), --indentLevel);\n            writer.println(wrapClose(SIGNONMSGSRSV1), --indentLevel);\n\n            writer.println(wrapOpen(getBankingMessageSetAggregate(account)), indentLevel++);\n            writer.println(wrapOpen(getResponse(account)), indentLevel++);\n            writer.println(wrapOpen(TRNUID) + \"1\", indentLevel);\n            writer.println(wrapOpen(STATUS), indentLevel++);\n            writer.println(wrapOpen(CODE) + \"0\", indentLevel);\n            writer.println(wrapOpen(SEVERITY) + \"INFO\", indentLevel);\n            writer.println(wrapClose(STATUS), --indentLevel);\n\n            // begin start of statement response\n            writer.println(wrapOpen(getStatementResponse(account)), indentLevel++);\n            writer.println(wrapOpen(CURDEF) + account.getCurrencyNode().getSymbol(), indentLevel);\n\n            // write account identification\n            writer.println(wrapOpen(getAccountFromAggregate(account)), indentLevel++);\n\n            switch (account.getAccountType()) {\n                case INVEST:\n                case MUTUAL:\n                    writer.println(wrapOpen(BROKERID), indentLevel); //  required for investment accounts, but jGnash does not manage a broker ID, normally a web URL\n                    break;\n                default:\n                    writer.println(wrapOpen(BANKID) + account.getBankId(), indentLevel); // savings and checking only\n                    break;\n            }\n\n            writer.println(wrapOpen(ACCTID) + account.getAccountNumber(), indentLevel);\n\n            // write the required account type\n            switch (account.getAccountType()) {\n                case CHECKING:\n                    writer.println(wrapOpen(ACCTTYPE) + CHECKING, indentLevel);\n                    break;\n                case ASSET:\n                case BANK:\n                case CASH:\n                    writer.println(wrapOpen(ACCTTYPE) + SAVINGS, indentLevel);\n                    break;\n                case CREDIT:\n                case LIABILITY:\n                    writer.println(wrapOpen(ACCTTYPE) + CREDITLINE, indentLevel);\n                    break;\n                case SIMPLEINVEST:\n                case MONEYMKRT:\n                    writer.println(wrapOpen(ACCTTYPE) + MONEYMRKT, indentLevel);\n                    break;\n                default:\n                    break;\n            }\n\n            writer.println(wrapClose(getAccountFromAggregate(account)), --indentLevel);\n\n            // begin start of transaction list\n            writer.println(wrapOpen(getTransactionList(account)), indentLevel++);\n            writer.println(wrapOpen(DTSTART) + encodeDate(startDate), indentLevel);\n            writer.println(wrapOpen(DTEND) + encodeDate(endDate), indentLevel);\n\n            // write the transaction list\n            if (account.getAccountType() == AccountType.INVEST || account.getAccountType() == AccountType.MUTUAL) {\n                writeInvestmentTransactions();\n            } else {\n                writeBankTransactions();\n            }\n\n            // end of transaction list\n            writer.println(wrapClose(getTransactionList(account)), --indentLevel);\n\n            // write ledger balance\n            writer.println(wrapOpen(LEDGERBAL), indentLevel++);\n            writer.println(wrapOpen(BALAMT) + account.getBalance(endDate).toPlainString(), indentLevel);\n            writer.println(wrapOpen(DTASOF) + encodeDate(exportDate), indentLevel);\n            writer.println(wrapClose(LEDGERBAL), --indentLevel);\n\n            // end of statement response\n            writer.println(wrapClose(getStatementResponse(account)), --indentLevel);\n            writer.println(wrapClose(getResponse(account)), --indentLevel);\n            writer.println(wrapClose(getBankingMessageSetAggregate(account)), --indentLevel);\n\n            // finished\n            writer.println(wrapClose(OFX), --indentLevel);\n        } catch (IOException e) {\n            Logger.getLogger(OfxExport.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    /**\n     * Writes all bank account transactions within the date range\n     */\n    private void writeBankTransactions() {\n        account.getTransactions(startDate, endDate).forEach(this::writeBankTransaction);\n    }\n\n    /**\n     * Writes all investment account transactions within the date range\n     */\n    private void writeInvestmentTransactions() {\n        for (final Transaction transaction : account.getTransactions(startDate, endDate)) {\n            if (transaction instanceof InvestmentTransaction) {\n                final InvestmentTransaction invTransaction = (InvestmentTransaction) transaction;\n\n                switch (invTransaction.getTransactionType()) {\n                    case ADDSHARE:\n                    case BUYSHARE:\n                        writeBuyStockTransaction(invTransaction);\n                        break;\n                    case REMOVESHARE:\n                    case SELLSHARE:\n                        writeSellStockTransaction(invTransaction);\n                        break;\n                    case DIVIDEND:\n                        writeDividendTransaction(invTransaction);\n                        break;\n                    case REINVESTDIV:\n                        writeReinvestStockTransaction(invTransaction);\n                        break;\n                    default:\n                        break;\n                }\n            } else {    // bank transaction, write it\n                indentedWriter.println(wrapOpen(INVBANKTRAN), indentLevel++);\n                writeBankTransaction(transaction);\n                indentedWriter.println(wrapClose(INVBANKTRAN), --indentLevel);\n            }\n        }\n    }\n\n    /**\n     * Writes one bank transaction\n     *\n     * @param transaction {@code Transaction} to write\n     */\n    private void writeBankTransaction(final Transaction transaction) {\n        indentedWriter.println(wrapOpen(STMTTRN), indentLevel++);\n        indentedWriter.println(wrapOpen(TRNTYPE)\n                + (transaction.getAmount(account).compareTo(BigDecimal.ZERO) >= 1 ? CREDIT : DEBIT), indentLevel);\n\n        indentedWriter.println(wrapOpen(DTPOSTED) + encodeDate(transaction.getLocalDate()), indentLevel);\n        indentedWriter.println(wrapOpen(TRNAMT) + transaction.getAmount(account).toPlainString(), indentLevel);\n        indentedWriter.println(wrapOpen(REFNUM) + transaction.getUuid(), indentLevel);\n        indentedWriter.println(wrapOpen(NAME) + transaction.getPayee(), indentLevel);\n        indentedWriter.println(wrapOpen(MEMO) + transaction.getMemo(), indentLevel);\n\n        // write the check number if applicable\n        if (account.getAccountType() == AccountType.CHECKING && !transaction.getNumber().isEmpty()) {\n            indentedWriter.println(wrapOpen(CHECKNUM) + transaction.getNumber(), indentLevel);\n        }\n\n        // write out the banks transaction id if previously imported\n        writeFitID(transaction);\n\n        // write out the account to\n        if (transaction.getTransactionType() == TransactionType.DOUBLEENTRY) {\n            final Account other = transaction.getTransactionEntries().get(0).getCreditAccount() != account\n                    ? transaction.getTransactionEntries().get(0).getCreditAccount()\n                    : transaction.getTransactionEntries().get(0).getDebitAccount();\n\n            if (other != null && other.getAccountNumber() != null && other.getAccountNumber().length() > 0) {\n                if (other.getAccountType() != AccountType.EXPENSE && other.getAccountType() != AccountType.INCOME) {\n                    writeAccountTo(other);\n                }\n            }\n        }\n\n        indentedWriter.println(wrapClose(STMTTRN), --indentLevel);\n    }\n\n    private void writeFitID(@NotNull final Transaction transaction) {\n        // write out the banks transaction id if previously imported\n        if (transaction.getFitid() != null && !transaction.getFitid().isEmpty()) {\n            indentedWriter.println(wrap(FITID, transaction.getFitid()), indentLevel);\n        } else {\n            indentedWriter.println(wrap(FITID, transaction.getUuid().toString()), indentLevel);\n        }\n    }\n\n    private void writeSecID(final SecurityNode node) {\n\n        // write security information\n        indentedWriter.println(wrapOpen(SECID), indentLevel++);\n\n        if (!node.getISIN().isEmpty()) {\n            indentedWriter.println(wrap(UNIQUEID, node.getISIN()), indentLevel);\n        } else {\n            indentedWriter.println(wrap(UNIQUEID, node.getSymbol()), indentLevel);\n        }\n\n        indentedWriter.println(wrap(UNIQUEIDTYPE, \"CUSIP\"), indentLevel);\n        indentedWriter.println(wrapClose(SECID), --indentLevel);\n    }\n\n    private void writeBuyStockTransaction(final InvestmentTransaction transaction) {\n        indentedWriter.println(wrapOpen(BUYSTOCK), indentLevel++);\n        indentedWriter.println(wrapOpen(INVBUY), indentLevel++);\n\n        indentedWriter.println(wrapOpen(INVTRAN), indentLevel++);\n\n        // write the FITID\n        writeFitID(transaction);\n\n        indentedWriter.println(wrap(DTTRADE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrap(DTSETTLE, encodeDate(transaction.getLocalDate())), indentLevel);\n\n        indentedWriter.println(wrapClose(INVTRAN), --indentLevel);\n\n        // write security information\n        writeSecID(transaction.getSecurityNode());\n\n        indentedWriter.println(wrap(UNITS, transaction.getQuantity().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(UNITPRICE, transaction.getPrice().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(COMMISSION, transaction.getFees().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(TOTAL, transaction.getTotal(account).toPlainString()), indentLevel);\n        indentedWriter.println(wrap(SUBACCTSEC, \"CASH\"), indentLevel);\n        indentedWriter.println(wrap(SUBACCTFUND, \"CASH\"), indentLevel);\n\n        indentedWriter.println(wrapClose(INVBUY), --indentLevel);\n        indentedWriter.println(wrap(BUYTYPE, \"BUY\"), indentLevel);\n        indentedWriter.println(wrapClose(BUYSTOCK), --indentLevel);\n    }\n\n    private void writeSellStockTransaction(final InvestmentTransaction transaction) {\n        indentedWriter.println(wrapOpen(SELLSTOCK), indentLevel++);\n        indentedWriter.println(wrapOpen(INVSELL), indentLevel++);\n\n        indentedWriter.println(wrapOpen(INVTRAN), indentLevel++);\n\n        // write the FITID\n        writeFitID(transaction);\n\n        indentedWriter.println(wrap(DTTRADE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrap(DTSETTLE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrapClose(INVTRAN), --indentLevel);\n\n        // write security information\n        writeSecID(transaction.getSecurityNode());\n\n        indentedWriter.println(wrap(UNITS, transaction.getQuantity().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(UNITPRICE, transaction.getPrice().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(COMMISSION, transaction.getFees().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(TOTAL, transaction.getTotal(account).toPlainString()), indentLevel);\n        indentedWriter.println(wrap(SUBACCTSEC, \"CASH\"), indentLevel);\n        indentedWriter.println(wrap(SUBACCTFUND, \"CASH\"), indentLevel);\n\n        indentedWriter.println(wrapClose(INVSELL), --indentLevel);\n        indentedWriter.println(wrap(SELLTYPE, \"SELL\"), indentLevel);\n        indentedWriter.println(wrapClose(SELLSTOCK), --indentLevel);\n    }\n\n    /**\n     * Reinvested transaction is a two part process.\n     * Need to show Income into cash and then the reinvestment from cash\n     *\n     * @param transaction transaction to write\n     */\n    private void writeReinvestStockTransaction(final InvestmentTransaction transaction) {\n\n        // Part one, show dividend income to cash\n        writeDividendTransaction(transaction);\n\n        // Part two, show reinvest from cash\n        indentedWriter.println(wrapOpen(REINVEST), indentLevel++);\n\n        indentedWriter.println(wrapOpen(INVTRAN), indentLevel++);\n\n        // write the FITID\n        writeFitID(transaction);\n\n        indentedWriter.println(wrap(DTTRADE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrap(DTSETTLE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrap(MEMO, \"Distribution reinvestment: \" + transaction.getSecurityNode().getSymbol()), indentLevel);\n        indentedWriter.println(wrapClose(INVTRAN), --indentLevel);\n\n        // write security information\n        writeSecID(transaction.getSecurityNode());\n        indentedWriter.println(wrap(INCOMETYPE, \"DIV\"), indentLevel);\n        indentedWriter.println(wrap(TOTAL, transaction.getTotal(account).abs().negate().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(SUBACCTSEC, \"CASH\"), indentLevel);\n\n        indentedWriter.println(wrap(UNITS, transaction.getQuantity().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(UNITPRICE, transaction.getPrice().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(COMMISSION, transaction.getFees().toPlainString()), indentLevel);\n        indentedWriter.println(wrapClose(REINVEST), --indentLevel);\n    }\n\n    private void writeDividendTransaction(final InvestmentTransaction transaction) {\n        indentedWriter.println(wrapOpen(INCOME), indentLevel++);\n\n        indentedWriter.println(wrapOpen(INVTRAN), indentLevel++);\n        writeFitID(transaction);  // write the FITID\n\n        indentedWriter.println(wrap(DTTRADE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrap(DTSETTLE, encodeDate(transaction.getLocalDate())), indentLevel);\n        indentedWriter.println(wrap(MEMO, \"Dividend: \" + transaction.getSecurityNode().getSymbol()), indentLevel);\n        indentedWriter.println(wrapClose(INVTRAN), --indentLevel);\n\n        // write security information\n        writeSecID(transaction.getSecurityNode());\n\n        indentedWriter.println(wrap(INCOMETYPE, \"DIV\"), indentLevel);\n        indentedWriter.println(wrap(TOTAL, transaction.getTotal(account).abs().toPlainString()), indentLevel);\n        indentedWriter.println(wrap(SUBACCTSEC, \"CASH\"), indentLevel);\n        indentedWriter.println(wrap(SUBACCTFUND, \"CASH\"), indentLevel);\n        indentedWriter.println(wrapClose(INCOME), --indentLevel);\n    }\n\n    private String encodeDate(final LocalDate date) {\n        return dateTimeFormatter.format(date) + \"000000\";\n    }\n\n    private static String wrapOpen(final String element) {\n        return \"<\" + element + \">\";\n    }\n\n    private static String wrapClose(final String element) {\n        return \"</\" + element + \">\";\n    }\n\n    private static String wrap(final String element, final String text) {\n        return wrapOpen(element) + text + wrapClose(element);\n    }\n\n    private void writeAccountTo(Account account) {\n        // write account identification\n        indentedWriter.println(wrapOpen(getAccountToAggregate(account)), indentLevel++);\n\n        switch (account.getAccountType()) {\n            case INVEST:\n            case MUTUAL:\n                indentedWriter.println(wrapOpen(BROKERID), indentLevel); //  required for investment accounts, but jGnash does not manage a broker ID, normally a web URL\n                break;\n            default:\n                indentedWriter.println(wrapOpen(BANKID) + account.getBankId(), indentLevel); // savings and checking only\n                break;\n        }\n\n        indentedWriter.println(wrapOpen(ACCTID) + account.getAccountNumber(), indentLevel);\n\n        // write the required account type\n        switch (account.getAccountType()) {\n            case CHECKING:\n                indentedWriter.println(wrapOpen(ACCTTYPE) + CHECKING, indentLevel);\n                break;\n            case ASSET:\n            case BANK:\n            case CASH:\n                indentedWriter.println(wrapOpen(ACCTTYPE) + SAVINGS, indentLevel);\n                break;\n            case CREDIT:\n            case LIABILITY:\n                indentedWriter.println(wrapOpen(ACCTTYPE) + CREDITLINE, indentLevel);\n                break;\n            case SIMPLEINVEST:\n            case MONEYMKRT:\n                indentedWriter.println(wrapOpen(ACCTTYPE) + MONEYMRKT, indentLevel);\n                break;\n            default:\n                break;\n        }\n\n        indentedWriter.println(wrapClose(getAccountToAggregate(account)), --indentLevel);\n\n    }\n\n    private static String getBankingMessageSetAggregate(final Account account) {\n        switch (account.getAccountType()) {\n            case ASSET:\n            case BANK:\n            case CASH:\n            case CHECKING:\n            case SIMPLEINVEST:\n                return BANKMSGSRSV1;\n            case CREDIT:\n            case LIABILITY:\n                return CREDITCARDMSGSRSV1;\n            case INVEST:\n            case MUTUAL:\n                return INVSTMTMSGSRSV1;\n            default:\n                return \"\";\n        }\n    }\n\n    private static String getResponse(final Account account) {\n        switch (account.getAccountType()) {\n            case ASSET:\n            case BANK:\n            case CASH:\n            case CHECKING:\n            case SIMPLEINVEST:\n                return STMTTRNRS;\n            case CREDIT:\n            case LIABILITY:\n                return CCSTMTTRNRS;\n            case INVEST:\n            case MUTUAL:\n                return INVSTMTTRNRS;\n            default:\n                return \"\";\n        }\n    }\n\n    private static String getStatementResponse(final Account account) {\n        switch (account.getAccountType()) {\n            case ASSET:\n            case BANK:\n            case CASH:\n            case CHECKING:\n            case SIMPLEINVEST:\n                return STMTRS;\n            case CREDIT:\n            case LIABILITY:\n                return CCSTMTRS;\n            case INVEST:\n            case MUTUAL:\n                return INVSTMTRS;\n            default:\n                return \"\";\n        }\n    }\n\n    private static String getAccountFromAggregate(final Account account) {\n        switch (account.getAccountType()) {\n            case ASSET:\n            case BANK:\n            case CASH:\n            case CHECKING:\n            case SIMPLEINVEST:\n                return BANKACCTFROM;\n            case CREDIT:\n            case LIABILITY:\n                return CCACCTFROM;\n            case INVEST:\n            case MUTUAL:\n                return INVACCTFROM;\n            default:\n                return \"\";\n        }\n    }\n\n    private static String getAccountToAggregate(final Account account) {\n        switch (account.getAccountType()) {\n            case ASSET:\n            case BANK:\n            case CASH:\n            case CHECKING:\n            case SIMPLEINVEST:\n                return BANKACCTTO;\n            case CREDIT:\n            case LIABILITY:\n                return CCACCTTO;\n            case INVEST:\n            case MUTUAL:\n                return INVACCTTO;\n            default:\n                return \"\";\n        }\n    }\n\n    private static String getTransactionList(final Account account) {\n        switch (account.getAccountType()) {\n            case INVEST:\n            case MUTUAL:\n                return INVTRANLIST;\n            default:\n                return BANKTRANLIST;\n        }\n    }\n\n    /**\n     * Support class to make writing indented SGML easier\n     */\n    private static class IndentedPrintWriter extends PrintWriter {\n\n        private static final String INDENT = \"  \";\n\n        IndentedPrintWriter(final Writer out) {\n            super(out);\n        }\n\n        void println(final String x, final int indentLevel) {\n            for (int i = 0; i < indentLevel; i++) {\n                write(INDENT);\n            }\n            println(x);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/BayesImportClassifier.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport jgnash.bayes.BayesClassifier;\nimport jgnash.engine.Account;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\n\n/**\n * Bayes classifier import utility methods\n *\n * @author Craig Cavanaugh\n */\npublic class BayesImportClassifier {\n\n    /**\n     * Utility class, private constructor\n     */\n    private BayesImportClassifier() {\n    }\n\n    public static void classifyTransactions(final List<? extends ImportTransaction> list,\n                                            final List<Transaction> transactions, final Account baseAccount) {\n\n        final BayesClassifier<Account> classifier = generateClassifier(transactions, baseAccount);\n\n        for (final ImportTransaction transaction : list) {\n            final StringBuilder builder = new StringBuilder();\n\n            builder.append(transaction.getPayee()).append(\" \");\n\n            if (transaction.getMemo() != null) {\n                builder.append(transaction.getMemo());\n            }\n\n            // reinvested dividends do not have a cash account\n            if (transaction.getTransactionType() != TransactionType.REINVESTDIV) {\n                transaction.setAccount(classifier.classify(builder.toString()));\n            }\n        }\n    }\n\n    private static BayesClassifier<Account> generateClassifier(List<Transaction> transactions,\n                                                               final Account baseAccount) {\n\n        final BayesClassifier<Account> classifier = new BayesClassifier<>(baseAccount);\n\n        for (final Transaction t : transactions) {\n            final Set<Account> accountSet = t.getAccounts();\n\n            accountSet.remove(baseAccount);\n\n            for (final Account account : accountSet) {\n                if (!t.getPayee().isEmpty()) {\n                    classifier.train(t.getPayee(), account);\n                }\n\n                if (!t.getMemo().isEmpty()) {\n                    classifier.train(t.getMemo(), account);\n                }\n            }\n        }\n\n        return classifier;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/DateFormat.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\n/**\n * @author Craig Cavanaugh\n */\npublic enum DateFormat {\n    US(\"mm/dd/yyyy\"),\n    EU(\"dd/mm/yyyy\");\n\n    private final String format;\n\n    DateFormat(String format) {\n        this.format = format;\n    }\n\n    @Override\n    public String toString() {\n        return format;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/GenericImport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Generic import utility methods\n *\n * @author Craig Cavanaugh\n * @author Arnout Engelen\n */\npublic class GenericImport {\n\n    private GenericImport() {\n    }\n\n    public static void importTransactions(@NotNull final List<? extends ImportTransaction> transactions,\n                                          @NotNull final Account baseAccount) {\n        Objects.requireNonNull(transactions);\n        Objects.requireNonNull(baseAccount);\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final ImportTransaction tran : transactions) {\n            Objects.requireNonNull(tran.getAccount());\n\n            if (tran.getState() == ImportState.NEW\n                        || tran.getState() == ImportState.NOT_EQUAL) { // do not import matched transactions\n\n                Transaction transaction;\n\n                if (tran.isInvestmentTransaction()) {\n                    if (baseAccount.getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                        System.out.println(\"Should be creating an investment transaction\");\n                    } else { // Signal an error\n                        System.out.println(\"The base account was not an investment account type\");\n                    }\n                }\n\n                if (baseAccount.equals(tran.getAccount())) { // single entry oTran\n                    transaction = TransactionFactory.generateSingleEntryTransaction(baseAccount, tran.getAmount(),\n                            tran.getDatePosted(), tran.getMemo(), tran.getPayee(), tran.getCheckNumber());\n                } else { // double entry\n                    if (tran.getAmount().signum() >= 0) {\n                        transaction = TransactionFactory.generateDoubleEntryTransaction(baseAccount, tran.getAccount(),\n                                tran.getAmount().abs(), tran.getDatePosted(), tran.getMemo(), tran.getPayee(),\n                                tran.getCheckNumber());\n                    } else {\n                        transaction = TransactionFactory.generateDoubleEntryTransaction(tran.getAccount(), baseAccount,\n                                tran.getAmount().abs(), tran.getDatePosted(), tran.getMemo(), tran.getPayee(),\n                                tran.getCheckNumber());\n                    }\n                }\n\n                transaction.setFitid(tran.getFITID());\n                engine.addTransaction(transaction);\n            }\n        }\n    }\n\n    /**\n     * Sets the match state of a list of imported transactions\n     *\n     * @param list        list of imported transactions\n     * @param baseAccount account to perform match against\n     */\n    public static void matchTransactions(final List<? extends ImportTransaction> list, @NotNull final Account baseAccount) {\n        Objects.requireNonNull(baseAccount);\n\n        for (final ImportTransaction importTransaction : list) {\n\n            // amount must always match\n            for (final Transaction tran : baseAccount.getSortedTransactionList()) {\n\n                // amounts must be comparably the same, do not use an equality check\n                if (tran.getAmount(baseAccount).compareTo(importTransaction.getAmount()) == 0) {\n\n                    // check for date match\n                    final LocalDate startDate;\n                    final LocalDate endDate;\n\n                    // we have a user initiated date, use a smaller window\n                    if ((importTransaction.getDateUser() != null)) {\n                        startDate = importTransaction.getDateUser().minusDays(1);\n                        endDate = importTransaction.getDateUser().plusDays(1);\n                    } else { // use the posted date with a larger window\n                        startDate = importTransaction.getDatePosted().minusDays(3);\n                        endDate = importTransaction.getDatePosted().plusDays(3);\n                    }\n\n                    if (DateUtils.after(tran.getLocalDate(), startDate) && DateUtils.before(tran.getLocalDate(), endDate)) {\n                        importTransaction.setState(ImportState.EQUAL);\n                        break;\n                    }\n\n\n                    // check for matching check number\n                    final String checkNumber = importTransaction.getCheckNumber();\n                    if (checkNumber != null && !checkNumber.isEmpty()) {\n                        if (tran.getNumber().equals(checkNumber)) {\n                            importTransaction.setState(ImportState.EQUAL);\n                            break;\n                        }\n                    }\n\n                    // check for matching fitid number\n                    final String id = importTransaction.getFITID();\n                    if (id != null && !id.isEmpty()) {\n                        if (tran.getFitid() != null && tran.getFitid().equals(id)) {\n                            importTransaction.setState(ImportState.EQUAL);\n                            break;\n                        }\n                    }\n\n                }\n            }\n        }\n    }\n\n    public static Account findFirstAvailableAccount() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final Account account : engine.getAccountList()) {\n            if (!account.isPlaceHolder() && !account.isLocked()) {\n                return account;\n            }\n        }\n\n        return null;\n    }\n\n    public static void importSecurities(final List<ImportSecurity> importSecurities, final CurrencyNode currencyNode) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final ImportSecurity importSecurity : importSecurities) {\n            if (ImportUtils.matchSecurity(importSecurity).isEmpty()) {   // Import only if a match is not found\n                final SecurityNode securityNode = ImportUtils.createSecurityNode(importSecurity, currencyNode);\n\n                // link the security node\n                importSecurity.setSecurityNode(securityNode);\n\n                engine.addSecurity(securityNode);\n\n                // if the ImportSecurity has pricing information, import it as well\n                importSecurity.getLocalDate().ifPresent(localDate -> importSecurity.getUnitPrice().ifPresent(price -> {\n                    SecurityHistoryNode securityHistoryNode = new SecurityHistoryNode(localDate, price, 0, price, price);\n\n                    engine.addSecurityHistory(securityNode, securityHistoryNode);\n                }));\n            } else {    // check to see if the cuspid needs to be updated\n\n                // link the security node\n                ImportUtils.matchSecurity(importSecurity).ifPresent(importSecurity::setSecurityNode);\n\n                ImportUtils.matchSecurity(importSecurity)\n                        .ifPresent(securityNode -> importSecurity.getId().ifPresent(securityId -> {\n                            if (securityNode.getISIN() == null || securityNode.getISIN().isEmpty()) {\n                                try {\n                                    final SecurityNode clone = (SecurityNode) securityNode.clone();\n                                    clone.setISIN(securityId);\n\n                                    engine.updateCommodity(securityNode, clone);\n\n                                    Logger.getLogger(GenericImport.class.getName()).info(\"Assigning CUSPID\");\n                                } catch (final CloneNotSupportedException e) {\n                                    Logger.getLogger(GenericImport.class.getName()).log(Level.SEVERE,\n                                            e.getLocalizedMessage(), e);\n                                }\n                            }\n                        }));\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ImportBank.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Common superclass for OFX and MT940 in\n * \n * @author Craig Cavanaugh\n * @author Arnout Engelen\n */\npublic class ImportBank<E extends ImportTransaction> {\n\n    private List<E> transactions = new ArrayList<>();\n\n    final protected List<ImportSecurity> securityList = new ArrayList<>();\n\n    public void addSecurity(final ImportSecurity importSecurity) {\n        securityList.add(importSecurity);\n    }\n\n    public void setTransactions(List<E> transactions) {\n        this.transactions = transactions;\n    }\n\n    /**\n     * Returns a mutable list of transactions.\n     *\n     * @return mutable List\n     */\n    public List<E> getTransactions() {\n        return transactions;\n    }\n\n    public void addTransaction(E transaction) {\n        transactions.add(transaction);\n    }\n\n    public List<ImportSecurity> getSecurityList() {\n        return securityList;\n    }\n\n    public boolean isInvestmentAccount() {\n\n        boolean result = false;\n\n        for (final E transaction : getTransactions()) {\n            if (transaction.isInvestmentTransaction()) {\n                result = true;\n                break;\n            }\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ImportFilter.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.util.EncodeDecode;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.OS;\n\nimport javax.script.Invocable;\nimport javax.script.ScriptEngine;\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport static jgnash.util.FileUtils.SEPARATOR;\n\n/**\n * Transaction Import filter.\n * <p>\n * This class is responsible for calling the supplied javascript file.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportFilter {\n\n    private final static String ENABLED_FILTERS = \"enabledFilters\";\n\n    private final static String IMPORT_SCRIPT_DIRECTORY_NAME = \"importScripts\";\n\n    private static final String JS_REGEX_PATTERN = \".*.js\";\n\n    private static final Logger logger = Logger.getLogger(ImportFilter.class.getName());\n\n    private static final String[] KNOWN_SCRIPTS = {\"/jgnash/convert/scripts/tidy.js\"};\n\n    private final ScriptEngine scriptEngine;\n\n    private final String script;\n\n    ImportFilter(final String script) {\n        scriptEngine = new ScriptEngineManager().getEngineByName(\"nashorn\");\n        this.script = script;\n        evalScript();\n    }\n\n    public static List<ImportFilter> getImportFilters() {\n        final List<ImportFilter> importFilterList = new ArrayList<>();\n\n        // known filters first\n        for (String knownScript : KNOWN_SCRIPTS) {\n            importFilterList.add(new ImportFilter(knownScript));\n        }\n\n        for (final Path path : FileUtils.getDirectoryListing(getUserImportScriptDirectory(), JS_REGEX_PATTERN)) {\n            importFilterList.add(new ImportFilter(path.toString()));\n        }\n\n        final String activeDatabase = EngineFactory.getActiveDatabase();\n        if (activeDatabase != null && !activeDatabase.startsWith(EngineFactory.REMOTE_PREFIX)) {\n            for (final Path path : FileUtils.getDirectoryListing(getBaseFileImportScriptDirectory(Paths.get(activeDatabase)), JS_REGEX_PATTERN)) {\n                importFilterList.add(new ImportFilter(path.toString()));\n            }\n        }\n\n        return importFilterList;\n    }\n\n    public static List<ImportFilter> getEnabledImportFilters() {\n        List<ImportFilter> filterList = new ArrayList<>();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final String string : EncodeDecode.decodeStringCollection(engine.getPreference(ENABLED_FILTERS))) {\n            filterList.add(new ImportFilter(string));\n        }\n\n        return filterList;\n    }\n\n    public static void saveEnabledImportFilters(final List<ImportFilter> filters) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        if (filters != null && filters.size() > 0) {\n            final List<String> scripts = filters.stream().map(ImportFilter::getScript).collect(Collectors.toList());\n            engine.setPreference(ENABLED_FILTERS, EncodeDecode.encodeStringCollection(scripts));\n        } else {\n            engine.setPreference(ENABLED_FILTERS, null);\n        }\n    }\n\n    private static Path getUserImportScriptDirectory() {\n\n        String scriptDirectory = System.getProperty(\"user.home\");\n\n        // decode to correctly handle spaces, etc. in the returned path\n        try {\n            scriptDirectory = URLDecoder.decode(scriptDirectory, StandardCharsets.UTF_8.name());\n        } catch (final UnsupportedEncodingException ex) {\n            logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n        }\n\n        if (OS.isSystemWindows()) {\n            scriptDirectory += SEPARATOR + \"AppData\" + SEPARATOR + \"Local\" + SEPARATOR\n                    + \"jgnash\" + SEPARATOR + IMPORT_SCRIPT_DIRECTORY_NAME;\n        } else { // unix, osx\n            scriptDirectory += SEPARATOR + \".jgnash\" + SEPARATOR + IMPORT_SCRIPT_DIRECTORY_NAME;\n        }\n\n        logger.log(Level.INFO, \"Import Script path: {0}\", scriptDirectory);\n\n\n        return Paths.get(scriptDirectory);\n    }\n\n    private static Path getBaseFileImportScriptDirectory(@NotNull final Path baseFile) {\n        if (baseFile.getParent() != null) {\n            return Paths.get(baseFile.getParent() + SEPARATOR + IMPORT_SCRIPT_DIRECTORY_NAME);\n        }\n\n        return null;\n    }\n\n    public String getScript() {\n        return script;\n    }\n\n    private void evalScript() {\n        try (final Reader reader = getReader()) {\n            scriptEngine.eval(reader);\n        } catch (final ScriptException | IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    public String processMemo(final String memo) {\n        try {\n            final Invocable invocable = (Invocable) scriptEngine;\n\n            final Object result = invocable.invokeFunction(\"processMemo\", memo);\n\n            return result.toString();\n        } catch (final ScriptException | NoSuchMethodException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n        return memo;\n    }\n\n    public String processPayee(final String payee) {\n        try {\n            final Invocable invocable = (Invocable) scriptEngine;\n\n            final Object result = invocable.invokeFunction(\"processPayee\", payee);\n\n            return result.toString();\n        } catch (final ScriptException | NoSuchMethodException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n        return payee;\n    }\n\n    public String getDescription() {\n        try {\n            final Invocable invocable = (Invocable) scriptEngine;\n\n            final Object result = invocable.invokeFunction(\"getDescription\", Locale.getDefault());\n\n            return result.toString();\n        } catch (final ScriptException | NoSuchMethodException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n        return \"\";\n    }\n\n    public void acceptTransaction(final ImportTransaction importTransaction) {\n        try {\n            final Invocable invocable = (Invocable) scriptEngine;\n\n            invocable.invokeFunction(\"acceptTransaction\", importTransaction);\n\n        } catch (final ScriptException | NoSuchMethodException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private Reader getReader() throws IOException {\n        if (Files.exists(Paths.get(script))) {\n            return Files.newBufferedReader(Paths.get(script));\n        }\n\n        return new InputStreamReader(\n                Objects.requireNonNull(ImportFilter.class.getResourceAsStream(script)), StandardCharsets.UTF_8);\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ImportSecurity.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Optional;\n\nimport jgnash.engine.SecurityNode;\n\n/**\n * Security Import Object\n *\n * @author Craig Cavanaugh\n */\npublic class ImportSecurity {\n\n    private String ticker;\n    private String securityName;\n    private BigDecimal unitPrice;\n    private LocalDate localDate;\n    private String id;\n    public String idType;\n    private String currency;\n    private BigDecimal currencyRate;\n\n    private SecurityNode securityNode;\n\n    @Override\n    public String toString() {\n        StringBuilder b = new StringBuilder();\n\n        b.append(\"ticker: \").append(getTicker()).append('\\n');\n        b.append(\"securityName: \").append(securityName).append('\\n');\n        b.append(\"unitPrice: \").append(unitPrice).append('\\n');\n        b.append(\"localDate: \").append(localDate).append('\\n');\n\n        if (id != null) {\n            b.append(\"id: \").append(id).append('\\n');\n        }\n\n        if (idType != null) {\n            b.append(\"idType: \").append(idType).append('\\n');\n        }\n\n        getCurrency().ifPresent(currency -> b.append(\"currency: \").append(currency).append('\\n'));\n\n        getCurrencyRate().ifPresent(rate -> b.append(\"currencyRate: \").append(rate).append('\\n'));\n\n        return b.toString();\n    }\n\n    public Optional<String> getId() {\n        return Optional.ofNullable(id);\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    Optional<String> getSecurityName() {\n        return Optional.ofNullable(securityName);\n    }\n\n    public void setSecurityName(final String securityName) {\n        this.securityName = securityName;\n    }\n\n    Optional<BigDecimal> getUnitPrice() {\n        return Optional.ofNullable(unitPrice);\n    }\n\n    public void setUnitPrice(final BigDecimal unitPrice) {\n        this.unitPrice = unitPrice;\n    }\n\n    public Optional<LocalDate> getLocalDate() {\n        return Optional.ofNullable(localDate);\n    }\n\n    public void setLocalDate(final LocalDate localDate) {\n        this.localDate = localDate;\n    }\n\n    public void setCurrencyRate(final BigDecimal unitPrice) {\n        this.currencyRate = unitPrice;\n    }\n\n    public Optional<BigDecimal> getCurrencyRate() {\n        return Optional.ofNullable(currencyRate);\n    }\n\n    public void setCurrency(final String currency) {\n        this.currency = currency;\n    }\n\n    public Optional<String> getCurrency() {\n        return Optional.ofNullable(currency);\n    }\n\n    /**\n     * Reference to the security node linked to this imported security node\n     */\n    public SecurityNode getSecurityNode() {\n        return securityNode;\n    }\n\n    public void setSecurityNode(SecurityNode securityNode) {\n        this.securityNode = securityNode;\n    }\n\n    public String getTicker() {\n        return ticker;\n    }\n\n    public void setTicker(final String ticker) {\n        this.ticker = ticker;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ImportState.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\n/**\n * @author Craig Cavanaugh\n */\npublic enum ImportState {\n    NEW,\n    EQUAL,\n    IGNORE,\n    NOT_EQUAL\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ImportTransaction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Objects;\nimport java.util.UUID;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Common interface for importing transactions from OFX, QIF, and mt940\n *\n * @author Craig Cavanaugh\n * @author Arnout Engelen\n * @author Nicolas Bouillon\n */\npublic class ImportTransaction implements Comparable<ImportTransaction> {\n\n    private final String uuid = UUID.randomUUID().toString();\n\n    /**\n     * The destination account\n     */\n    private Account account;\n\n    /**\n     * Account for dividends and gains/losses from an investment transaction\n     */\n    private Account gainsAccount;\n\n    /**\n     * Account for investment expenses\n     */\n    private Account feesAccount;\n\n    private BigDecimal amount = BigDecimal.ZERO;\n\n    private String checkNumber = \"\"; // check number (?)\n\n    @NotNull\n    private LocalDate datePosted = LocalDate.now();\n\n    @Nullable\n    private LocalDate dateUser = null;\n\n    private String memo = \"\"; // memo\n\n    @NotNull\n    private String payee = \"\";\n\n    // OFX\n    private String payeeId;\n\n    private ImportState state = ImportState.NEW;\n\n    // OFX, Financial Institution transaction ID\n    private String FITID;\n\n    // OFX\n    private String securityId;\n\n    // OFX\n    private String securityType;\n\n    private BigDecimal units = BigDecimal.ZERO;\n\n    private BigDecimal unitPrice = BigDecimal.ZERO;\n\n    private BigDecimal commission = BigDecimal.ZERO;\n\n    private BigDecimal fees = BigDecimal.ZERO;\n\n    // OFX, Type of income for investment transaction\n    private String incomeType;\n\n    private boolean taxExempt = false;\n\n    // OFX\n    private TransactionType transactionType = TransactionType.SINGLENTRY;  // single entry by default\n\n    // OFX\n    private String transactionTypeDescription;\n\n    // OFX\n    private String SIC;\n\n    // OFX\n    private String refNum;\n\n    // OFX\n    private String subAccount;\n\n    private String currency;\n\n    // OFX, transfer account id\n    private String accountTo;\n\n    /**\n     * @return returns the destination account\n     */\n    public Account getAccount() {\n        return account;\n    }\n\n    public void setAccount(final Account account) {\n        this.account = account;\n    }\n\n    /**\n     * Depending on the implementation a unique ID may be provided that can be used to detect\n     * duplication of prior imported transactions.\n     *\n     * @return transaction id\n     */\n    public String getFITID() {\n        return FITID;\n    }\n\n    public void setFITID(String FITID) {\n        this.FITID = FITID;\n    }\n\n    /**\n     * Deposits get positive 'amounts', withdrawals negative\n     *\n     * @return transaction amount\n     */\n    public BigDecimal getAmount() {\n        return amount;\n    }\n\n    public void setAmount(BigDecimal amount) {\n        this.amount = amount;\n    }\n\n    @NotNull\n    public LocalDate getDatePosted() {\n        return datePosted;\n    }\n\n    public void setDatePosted(@NotNull LocalDate datePosted) {\n        this.datePosted = datePosted;\n    }\n\n    /**\n     * Date user initiated the transaction, optional, may be null\n     *\n     * @return date transaction was initiated\n     */\n    @Nullable\n    public LocalDate getDateUser() {\n        return dateUser;\n    }\n\n    public void setDateUser(@Nullable LocalDate dateUser) {\n        this.dateUser = dateUser;\n    }\n\n    public String getCheckNumber() {\n        return checkNumber;\n    }\n\n    public void setCheckNumber(final String checkNumber) {\n        this.checkNumber = checkNumber;\n    }\n\n    public String getMemo() {\n        return memo;\n    }\n\n    public void setMemo(String memo) {\n        this.memo = memo;\n    }\n\n    public ImportState getState() {\n        return state;\n    }\n\n    public void setState(ImportState state) {\n        this.state = state;\n    }\n\n    @NotNull\n    public String getPayee() {\n        return payee;\n    }\n\n    public void setPayee(@NotNull String payee) {\n        Objects.requireNonNull(payee);\n\n        this.payee = payee;\n    }\n\n    public String getSecurityId() {\n        return securityId;\n    }\n\n    public void setSecurityId(String securityId) {\n        this.securityId = securityId;\n    }\n\n    @Override\n    public int compareTo(@NotNull final ImportTransaction importTransaction) {\n        if (importTransaction == this) {\n            return 0;\n        }\n\n        int result = getDatePosted().compareTo(importTransaction.getDatePosted());\n        if (result != 0) {\n            return result;\n        }\n\n        result = payee.compareTo(importTransaction.payee);\n        if (result != 0) {\n            return result;\n        }\n\n        return Integer.compare(hashCode(), importTransaction.hashCode());\n    }\n\n    @Override\n    public boolean equals(final Object that) {\n        if (this == that) {\n            return true;\n        }\n\n        if (that == null || getClass() != that.getClass()) {\n            return false;\n        }\n\n        return Objects.equals(uuid, ((ImportTransaction) that).uuid);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(uuid);\n    }\n\n    /**\n     * Investment transaction units\n     */\n    public BigDecimal getUnits() {\n        return units;\n    }\n\n    public void setUnits(final BigDecimal units) {\n        this.units = units;\n    }\n\n    /**\n     * Investment transaction unit price\n     */\n    public BigDecimal getUnitPrice() {\n        return unitPrice;\n    }\n\n    public void setUnitPrice(final BigDecimal unitPrice) {\n        this.unitPrice = unitPrice;\n    }\n\n    /**\n     * Investment transaction commission\n     */\n    public BigDecimal getCommission() {\n        return commission;\n    }\n\n    public void setCommission(final BigDecimal commission) {\n        this.commission = commission;\n    }\n\n    public boolean isTaxExempt() {\n        return taxExempt;\n    }\n\n    public void setTaxExempt(boolean taxExempt) {\n        this.taxExempt = taxExempt;\n    }\n\n    public String getSecurityType() {\n        return securityType;\n    }\n\n    public void setSecurityType(final String securityType) {\n        this.securityType = securityType;\n    }\n\n    public boolean isInvestmentTransaction() {\n        return getSecurityId() != null;\n    }\n\n    public String getIncomeType() {\n        return incomeType;\n    }\n\n    public void setIncomeType(String incomeType) {\n        this.incomeType = incomeType;\n    }\n\n    @NotNull public BigDecimal getFees() {\n        return fees;\n    }\n\n    public void setFees(@NotNull final BigDecimal fees) {\n        this.fees = fees;\n    }\n\n    /**\n     * The parser may establish a transaction type when imported.\n     *\n     * @return {@code TransactionType}\n     */\n    @NotNull\n    public TransactionType getTransactionType() {\n        return transactionType;\n    }\n\n    public void setTransactionType(@NotNull final TransactionType transactionType) {\n        this.transactionType = transactionType;\n    }\n\n    /**\n     * OFX defines descriptive transaction types\n     */\n    public String getTransactionTypeDescription() {\n        return transactionTypeDescription;\n    }\n\n    public void setTransactionTypeDescription(final String transactionTypeDescription) {\n        this.transactionTypeDescription = transactionTypeDescription;\n    }\n\n    /**\n     * Standard Industry Code\n     * <p>\n     * Could be use used for automatic expense and income assignment.  Typically a 4 digit numeric, but OFX allows 6\n     */\n    public String getSIC() {\n        return SIC;\n    }\n\n    public void setSIC(String SIC) {\n        this.SIC = SIC;\n    }\n\n    /**\n     * Reference number that uniquely identifies the transaction. May be used in\n     * addition to or instead of a {@link #getCheckNumber()}\n     */\n    public String getRefNum() {\n        return refNum;\n    }\n\n    public void setRefNum(String refNum) {\n        this.refNum = refNum;\n    }\n\n    /**\n     * Some OFX based systems will assign an ID to a Payee.\n     * <p>\n     * The ID would correspond to a Payee List identified by <PAYEELSTID> (not implemented)\n     */\n    public String getPayeeId() {\n        return payeeId;\n    }\n\n    public void setPayeeId(String payeeId) {\n        this.payeeId = payeeId;\n    }\n\n    /**\n     * The sub-account for cash transfer, typically CASH, but could be MARGIN, SHORT, or OTHER\n     * <p>\n     * <SUBACCTFROM>, <SUBACCTFUND>, <SUBACCTSEC>, <SUBACCTTO>\n     */\n    public String getSubAccount() {\n        return subAccount;\n    }\n\n    public void setSubAccount(String subAccount) {\n        this.subAccount = subAccount;\n    }\n\n    public String getCurrency() {\n        return currency;\n    }\n\n    public void setCurrency(String currency) {\n        this.currency = currency;\n    }\n\n    /**\n     * Account for gains or losses from an investment transaction\n     */\n    public Account getGainsAccount() {\n        return gainsAccount;\n    }\n\n    public void setGainsAccount(final Account gainsAccount) {\n        this.gainsAccount = gainsAccount;\n    }\n\n    /**\n     * Account for investment expenses\n     */\n    public Account getFeesAccount() {\n        return feesAccount;\n    }\n\n    public void setFeesAccount(Account feesAccount) {\n        this.feesAccount = feesAccount;\n    }\n\n    @NotNull\n    public String getToolTip() {\n        if (isInvestmentTransaction()) {\n            return units.toString() + \" @ \" + unitPrice.toString();\n        }\n        return \"\";\n    }\n\n    @Override\n    public String toString() {\n        return getTransactionTypeDescription() + \", \" +\n                getTransactionType() + \", \" +\n                getDatePosted() + \", \" +\n                getAmount() + \", \" +\n                getFITID() + \", \" +\n                getSIC() + \", \" +\n                getPayee() + \", \" +\n\n                getMemo() + \", \" +\n                getCheckNumber() + \", \" +\n                getRefNum() + \", \" +\n                getPayeeId() + \", \" +\n                getCurrency();\n    }\n\n    public String getAccountTo() {\n        return accountTo;\n    }\n\n    public void setAccountTo(String accountTo) {\n        this.accountTo = accountTo;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ImportUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.SecurityNode;\n\n/**\n * Various utility methods used when importing transactions\n *\n * @author Craig Cavanaugh\n */\npublic class ImportUtils {\n\n    private ImportUtils() {\n    }\n\n    public static Account getRootExpenseAccount() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        return searchForRootType(engine.getRootAccount(), AccountType.EXPENSE);\n    }\n\n    public static Account getRootIncomeAccount() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        return searchForRootType(engine.getRootAccount(), AccountType.INCOME);\n    }\n\n    /**\n     * Matches an ImportTransaction to an Account based on an AccountTo tag if it exists\n     * @param importTransaction Import transaction to test\n     * @return Account if found, null otherwise\n     */\n    public static Account matchAccount(final ImportTransaction importTransaction) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final String number = importTransaction.getAccountTo();\n\n        Account account = null;\n\n        if (number != null) {\n            for (final Account a : engine.getAccountList()) {\n                if (a.getAccountNumber().equals(number)) {\n                    account = a;\n                    break;\n                }\n            }\n        }\n\n        return account;\n    }\n\n    private static Account searchForRootType(final Account account, final AccountType accountType) {\n        Account result = null;\n\n        // search immediate top level accounts\n        for (Account a : account.getChildren()) {\n            if (a.getAccountType().equals(accountType)) {\n                return a;\n            }\n        }\n\n        // recursive search\n        for (Account a : account.getChildren()) {\n            result = searchForRootType(a, accountType);\n            if (result != null) {\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    static Optional<SecurityNode> matchSecurity(final ImportSecurity security) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final SecurityNode securityNode : engine.getSecurities()) {\n            if (securityNode.getSymbol().equals(security.getTicker())) {\n                return Optional.of(securityNode);\n            }\n        }\n        return Optional.empty();\n    }\n\n    static SecurityNode createSecurityNode(final ImportSecurity security, final CurrencyNode currencyNode) {\n        final SecurityNode securityNode = new SecurityNode(currencyNode);\n\n        securityNode.setSymbol(security.getTicker());\n        securityNode.setScale(currencyNode.getScale());\n\n        security.getSecurityName().ifPresent(securityNode::setDescription);\n        security.getId().ifPresent(securityNode::setISIN);\n\n        return securityNode;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ofx/OfxBank.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\n\nimport jgnash.convert.importat.ImportBank;\nimport jgnash.convert.importat.ImportSecurity;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.util.Nullable;\n\n/**\n * OFX Bank Object\n *\n * @author Craig Cavanaugh\n * @author Nicolas Bouillon\n */\npublic class OfxBank extends ImportBank<ImportTransaction> {\n\n    public String currency;\n\n    String bankId;\n\n    /**\n     * Branch identifier. May be required for some non-US banks\n     */\n    String branchId;\n\n    public String accountId;\n\n    String accountType;\n\n    LocalDate dateStart;\n\n    LocalDate dateEnd;\n\n    BigDecimal ledgerBalance;\n\n    LocalDate ledgerBalanceDate;\n\n    BigDecimal availBalance;\n\n    LocalDate availBalanceDate;\n\n    int statusCode;\n\n    String statusSeverity;\n\n    @Nullable\n    String statusMessage;\n\n    @Override\n    public String toString() {\n        StringBuilder b = new StringBuilder();\n\n        b.append(\"statusCode: \").append(statusCode).append('\\n');\n        b.append(\"statusSeverity: \").append(statusSeverity).append('\\n');\n        b.append(\"statusMessage: \").append(statusMessage).append('\\n');\n        b.append(\"currency: \").append(currency).append('\\n');\n        b.append(\"bankId: \").append(bankId).append('\\n');\n        b.append(\"branchId: \").append(branchId).append('\\n');\n        b.append(\"accountId: \").append(accountId).append('\\n');\n        b.append(\"accountType: \").append(accountType).append('\\n');\n        b.append(\"dateStart: \").append(dateStart).append('\\n');\n        b.append(\"dateEnd: \").append(dateEnd).append('\\n');\n        b.append(\"ledgerBalance: \").append(ledgerBalance).append('\\n');\n        b.append(\"ledgerBalanceDate: \").append(ledgerBalanceDate).append('\\n');\n\n        if (availBalance != null) {\n            b.append(\"availBalance: \").append(availBalance).append('\\n');\n        }\n\n        if (availBalanceDate != null) {\n            b.append(\"availBalanceDate: \").append(availBalanceDate).append('\\n');\n        }\n\n        for (final ImportTransaction t : getTransactions()) {\n            b.append(t).append('\\n');\n        }\n\n        for (final ImportSecurity importSecurity : securityList) {\n            b.append(importSecurity.toString()).append('\\n');\n        }\n\n        return b.toString();\n    }\n\n    /* <CURDEF>USD\n    <BANKACCTFROM>\n    <BANKID>074914229\n    <ACCTID>10076164\n    <ACCTTYPE>CHECKING\n    </BANKACCTFROM>\n     */\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ofx/OfxImport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport jgnash.convert.common.OfxTags;\nimport jgnash.convert.importat.ImportSecurity;\nimport jgnash.convert.importat.ImportState;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionTag;\n\n/**\n * OfxImport utility methods\n *\n * @author Craig Cavanaugh\n */\npublic class OfxImport {\n\n    /**\n     * Private constructor, utility class\n     */\n    private OfxImport() {\n    }\n\n    public static void importTransactions(final OfxBank ofxBank, final Account baseAccount) {\n        Objects.requireNonNull(ofxBank.getTransactions());\n        Objects.requireNonNull(baseAccount);\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final ImportTransaction tran : ofxBank.getTransactions()) {\n\n            // do not import matched transactions\n            if (tran.getState() == ImportState.NEW || tran.getState() == ImportState.NOT_EQUAL) {\n                Transaction transaction = null;\n\n                if (tran.isInvestmentTransaction()) {\n                    if (baseAccount.getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                        transaction = importInvestmentTransaction(ofxBank, tran, baseAccount);\n\n                        if (transaction != null) {\n\n                            // check and add the security node to the account if not present\n                            if (!baseAccount.containsSecurity(((InvestmentTransaction) transaction).getSecurityNode())) {\n                                engine.addAccountSecurity(((InvestmentTransaction) transaction).getInvestmentAccount(),\n                                        ((InvestmentTransaction) transaction).getSecurityNode());\n                            }\n                        }\n\n                    } else { // Signal an error\n                        System.out.println(\"Base account was not an investment account type\");\n                    }\n                } else {\n                    if (baseAccount.equals(tran.getAccount())) { // single entry oTran\n                        transaction = TransactionFactory.generateSingleEntryTransaction(baseAccount, tran.getAmount(),\n                                tran.getDatePosted(), tran.getMemo(), tran.getPayee(), tran.getCheckNumber());\n                    } else { // double entry\n                        if (tran.getAmount().signum() >= 0) {\n                            transaction = TransactionFactory.generateDoubleEntryTransaction(baseAccount, tran.getAccount(),\n                                    tran.getAmount().abs(), tran.getDatePosted(), tran.getMemo(), tran.getPayee(),\n                                    tran.getCheckNumber());\n                        } else {\n                            transaction = TransactionFactory.generateDoubleEntryTransaction(tran.getAccount(), baseAccount,\n                                    tran.getAmount().abs(), tran.getDatePosted(), tran.getMemo(), tran.getPayee(),\n                                    tran.getCheckNumber());\n                        }\n                    }\n                }\n\n                // add the new transaction\n                if (transaction != null) {\n                    transaction.setFitid(tran.getFITID());\n                    engine.addTransaction(transaction);\n                }\n            }\n        }\n    }\n\n    private static InvestmentTransaction importInvestmentTransaction(final OfxBank ofxBank, final ImportTransaction ofxTransaction,\n                                                                     final Account investmentAccount) {\n\n        final SecurityNode securityNode = matchSecurity(ofxBank, ofxTransaction.getSecurityId());\n        final String memo = ofxTransaction.getMemo();\n        final LocalDate datePosted = ofxTransaction.getDatePosted();\n\n        final BigDecimal units = ofxTransaction.getUnits();\n        final BigDecimal unitPrice = ofxTransaction.getUnitPrice();\n\n        final Account incomeAccount = ofxTransaction.getGainsAccount();\n        final Account fessAccount = ofxTransaction.getFeesAccount();\n\n        Account cashAccount = ofxTransaction.getAccount();\n\n        // force use of cash balance\n        if (OfxTags.CASH.equals(ofxTransaction.getSubAccount())) {\n            cashAccount = investmentAccount;\n        }\n\n        final List<TransactionEntry> fees = new ArrayList<>();\n        final List<TransactionEntry> gains = new ArrayList<>();\n\n        if (ofxTransaction.getCommission().compareTo(BigDecimal.ZERO) != 0) {\n            final TransactionEntry transactionEntry = new TransactionEntry(fessAccount, ofxTransaction.getCommission().negate());\n            transactionEntry.setTransactionTag(TransactionTag.INVESTMENT_FEE);\n            fees.add(transactionEntry);\n        }\n\n        if (ofxTransaction.getFees().compareTo(BigDecimal.ZERO) != 0) {\n            final TransactionEntry transactionEntry = new TransactionEntry(fessAccount, ofxTransaction.getFees().negate());\n            transactionEntry.setTransactionTag(TransactionTag.INVESTMENT_FEE);\n            fees.add(transactionEntry);\n        }\n\n        InvestmentTransaction transaction = null;\n\n        if (securityNode != null) {\n            switch (ofxTransaction.getTransactionType()) {\n                case DIVIDEND:\n                    final BigDecimal dividend = ofxTransaction.getAmount();\n\n                    transaction = TransactionFactory.generateDividendXTransaction(incomeAccount, investmentAccount, cashAccount,\n                            securityNode, dividend, dividend, dividend, datePosted, memo);\n                    break;\n                case REINVESTDIV:\n                    // Create a gains entry of an account other than the investment account has been selected\n                    if (incomeAccount != investmentAccount) {\n                        final TransactionEntry gainsEntry = TransactionFactory.createTransactionEntry(incomeAccount,\n                                investmentAccount, ofxTransaction.getAmount().negate(), memo, TransactionTag.GAIN_LOSS);\n                        gains.add(gainsEntry);\n                    }\n\n                    transaction = TransactionFactory.generateReinvestDividendXTransaction(investmentAccount, securityNode,\n                            unitPrice, units, datePosted, memo, fees, gains);\n                    break;\n                case BUYSHARE:\n                    transaction = TransactionFactory.generateBuyXTransaction(cashAccount, investmentAccount, securityNode,\n                            unitPrice, units, BigDecimal.ONE, datePosted, memo, fees);\n                    break;\n                case SELLSHARE:\n                    transaction = TransactionFactory.generateSellXTransaction(cashAccount, investmentAccount, securityNode,\n                            unitPrice, units, BigDecimal.ONE, datePosted, memo, fees, gains);\n                    break;\n                default:\n            }\n        }\n\n        return transaction;\n    }\n\n    private static SecurityNode matchSecurity(final OfxBank ofxBank, final String securityId) {\n\n        SecurityNode securityNode = null;\n\n        for (final ImportSecurity importSecurity : ofxBank.getSecurityList()) {\n            if (importSecurity.getId().isPresent()) {\n                if (importSecurity.getId().get().equals(securityId)) {\n                    securityNode = importSecurity.getSecurityNode();\n                    break;\n                }\n            }\n        }\n\n        return securityNode;\n    }\n\n    public static Account matchAccount(final OfxBank bank) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final String number = bank.accountId;\n        final CurrencyNode node = engine.getCurrency(bank.currency);\n\n        Account account = null;\n\n        if (node != null) {\n            for (Account a : engine.getAccountList()) {\n                if (a.getAccountNumber() != null && a.getAccountNumber().equals(number) && a.getCurrencyNode().equals(node)) {\n                    account = a;\n                    break;\n                }\n            }\n        } else if (number != null) {\n            for (Account a : engine.getAccountList()) {\n                if (a.getAccountNumber().equals(number)) {\n                    account = a;\n                    break;\n                }\n            }\n        }\n\n        return account;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ofx/OfxV1ToV2.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport jgnash.util.FileMagic;\n\nimport static jgnash.util.LogUtil.logSevere;\nimport static jgnash.convert.importat.ofx.Sanitize.sanitize;\n\n/**\n * Utility class to convert OFX version 1 (SGML) to OFX version 2 (XML)\n *\n * @author Craig Cavanaugh\n */\nclass OfxV1ToV2 {\n\n    private static final int READ_AHEAD_LIMIT = 2048;\n\n    /*\n    public static void main(final String[] args) {\n        if (args.length == 2) {\n            Path input = Paths.get(args[0]);\n            Path output = Paths.get(args[1]);\n\n            if (Files.exists(input)) {\n                try {\n                    Files.write(output, convertToXML(input).getBytes());\n                } catch (final IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }*/\n\n    static String convertToXML(final Path path) {\n        String encoding = FileMagic.getOfxV1Encoding(path);\n\n        Logger.getLogger(OfxV1ToV2.class.getName()).log(Level.INFO, \"OFX Version 1 file encoding was {0}\", encoding);\n\n        return convertSgmlToXML(readFile(path, encoding));\n    }\n\n    static String convertToXML(final InputStream stream) {\n        return convertSgmlToXML(readFile(stream, System.getProperty(\"file.encoding\")));\n    }\n\n    private static String convertSgmlToXML(final String sgml) {\n        StringBuilder xml = new StringBuilder(sgml);\n\n        int readPos = 0;\n        int tagEnd = 0;\n\n        while (readPos < xml.length() && readPos != -1) {\n            String tag;\n\n            readPos = xml.indexOf(\"<\", tagEnd);\n\n            if (readPos != -1) {\n                tagEnd = xml.indexOf(\">\", readPos);\n\n                if (tagEnd != -1) {\n                    tag = xml.substring(readPos + 1, tagEnd);\n\n                    if (!tag.startsWith(\"/\")) {\n                        if (xml.indexOf(\"</\" + tag + \">\", tagEnd) == -1) {\n\n                            readPos = xml.indexOf(\"<\", tagEnd);\n                            xml.insert(readPos, \"</\" + tag + \">\");\n                        }\n                    }\n                } else {\n                    readPos = -1;\n                }\n            }\n        }\n\n        return sanitize(xml.toString());\n    }\n\n    private static String concat(final Collection<String> strings) {\n        StringBuilder b = new StringBuilder();\n\n        for (String s : strings) {\n            b.append(s.trim());\n        }\n\n        return b.toString();\n    }\n\n    /**\n     * Munch through the header one character at a time. Do not assume clean\n     * formatting or EOL characters.\n     *\n     * @param reader {@code BufferedReader}\n     * @throws IOException thrown if IO error occurs\n     */\n    private static void consumeHeader(final BufferedReader reader) throws IOException {\n\n        Logger logger = Logger.getLogger(OfxV1ToV2.class.getName());\n\n        while (true) {\n            reader.mark(READ_AHEAD_LIMIT);\n\n            int character = reader.read();\n\n            if (character >= 0) {\n                if (character == '<') {\n                    reader.reset();\n                    logger.info(\"readHeader() Complete\");\n                    break;\n                }\n            } else {\n                break;\n            }\n        }\n    }\n\n    /**\n     * Reads a file, strips the header and reads the SGML content into one large\n     * string\n     *\n     * @param stream input stream\n     * @param characterSet assumed character set for the file being converted\n     * @return a String with the SGML content and header removed\n     */\n    private static String readFile(final InputStream stream, final String characterSet) {\n\n        if (stream == null) {\n            logSevere(OfxV1ToV2.class, \"InputStream was null\");\n            return null;\n        }\n\n        List<String> strings = new ArrayList<>();\n\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, characterSet))) {\n            // consume the Ofx1 header\n            consumeHeader(reader);\n\n            String line = reader.readLine();\n\n            while (line != null) {\n\n                line = line.trim();\n\n                if (!line.isEmpty()) {\n                    strings.add(line);\n                }\n\n                line = reader.readLine();\n            }\n        } catch (final IOException e) {\n            logSevere(OfxV1ToV2.class, e);\n        }\n\n        return concat(strings);\n    }\n\n    private static String readFile(final Path path, final String characterSet) {\n    \ttry (final InputStream stream = new BufferedInputStream(Files.newInputStream(path))) {\n    \t\treturn readFile(stream, characterSet);\n    \t} catch (final IOException e) {\n            logSevere(OfxV1ToV2.class, e);\n            return \"\";\n\t\t}     \t    \t     \n    }\n\n    private OfxV1ToV2() {\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ofx/OfxV2Parser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.text.ParseException;\nimport java.time.LocalDate;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.logging.FileHandler;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.logging.SimpleFormatter;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport javax.xml.namespace.QName;\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamConstants;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamReader;\n\nimport jgnash.convert.common.OfxTags;\nimport jgnash.convert.importat.ImportSecurity;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.FileMagic;\nimport jgnash.util.NotNull;\n\nimport static jgnash.convert.importat.ofx.Sanitize.sanitize;\n\n/**\n * StAX based parser for 2.x OFX (XML) files.\n * <p>\n * This parser will intentionally absorb higher level elements and drop through to simplify and reduce code.\n *\n * @author Craig Cavanaugh\n */\npublic class OfxV2Parser implements OfxTags {\n\n    private static final Logger logger = Logger.getLogger(\"OfxV2Parser\");\n\n    private static final String EXTRA_SPACE_REGEX = \"\\\\s+\";\n\n    private static final String ENCODING = StandardCharsets.UTF_8.name();\n\n    private OfxBank bank;\n\n    /**\n     * Default language is assumed to be English unless the import file defines it\n     */\n    private String language = \"ENG\";\n\n    private int statusCode;\n\n    private String statusSeverity;\n\n    /**\n     * Status message from sign-on process\n     */\n    private String statusMessage;\n\n    private final Pattern extraSpaceRegex = Pattern.compile(EXTRA_SPACE_REGEX);\n\n    /**\n     * Support class\n     */\n    private static class AccountInfo {\n        String bankId;\n        String accountId;\n        String accountType;\n        String branchId;\n    }\n\n    static void enableDetailedLogFile() {\n        try {\n            final Handler fh = new FileHandler(\"%t/jgnash-ofx.log\", false);\n            fh.setFormatter(new SimpleFormatter());\n            logger.addHandler(fh);\n            logger.setLevel(Level.ALL);\n        } catch (final IOException ioe) {\n            logger.severe(ResourceUtils.getString(\"Message.Error.LogFileHandler\"));\n        }\n    }\n\n    public static OfxBank parse(@NotNull final Path file) throws Exception {\n\n        final OfxV2Parser parser = new OfxV2Parser();\n\n        if (FileMagic.isOfxV1(file)) {\n            logger.info(\"Parsing OFX Version 1 file\");\n            parser.parse(OfxV1ToV2.convertToXML(file), FileMagic.getOfxV1Encoding(file));\n        } else if (FileMagic.isOfxV2(file)) {\n            logger.info(\"Parsing OFX Version 2 file\");\n            parser.parseFile(file);\n        } else {\n            logger.info(\"Unknown OFX Version\");\n        }\n\n        if (parser.getBank() == null) {\n            throw new Exception(\"Bank import failed\");\n        }\n\n        return postProcess(parser.getBank());\n    }\n\n    /**\n     * Post processes the OFX transactions for import.  Income transactions with a reinvestment transaction will be\n     * stripped out. jGnash has a reinvested dividend transaction that reduces overall transaction count.\n     *\n     * @param ofxBank OfxBank to process\n     * @return OfxBank with post processed transactions\n     */\n    private static OfxBank postProcess(final OfxBank ofxBank) {\n        // Clone the original list\n        final List<ImportTransaction> importTransactions = ofxBank.getTransactions();\n\n        // Create a list of Reinvested dividends\n        final List<ImportTransaction> reinvestedDividends = importTransactions.stream()\n                                                                    .filter(importTransaction -> importTransaction.getTransactionType() == TransactionType.REINVESTDIV)\n                                                                    .collect(Collectors.toList());\n\n        // Search through the list and remove matching income transactions\n        for (final ImportTransaction reinvestDividend : reinvestedDividends) {\n\n            final Iterator<ImportTransaction> iterator = importTransactions.iterator();\n\n            while (iterator.hasNext()) {\n                final ImportTransaction otherTran = iterator.next();\n\n                // if this was OFX income and the securities match and the amount match, remove the transaction\n                if (reinvestDividend != otherTran && OfxTags.INCOME.equals(otherTran.getTransactionTypeDescription())) {\n                    if (otherTran.getAmount().compareTo(reinvestDividend.getAmount().abs()) == 0) {\n                        if (otherTran.getSecurityId().equals(reinvestDividend.getSecurityId())) {\n                            iterator.remove();  // remove it\n                            // reverse sign\n                            reinvestDividend.setAmount(reinvestDividend.getAmount().abs());\n                        }\n                    }\n                }\n            }\n        }\n\n        return ofxBank;\n    }\n\n    /**\n     * Parse a date. Time zone and seconds are ignored\n     * <p>\n     * YYYYMMDDHHMMSS.XXX [gmt offset:tz name]\n     *\n     * @param date String form of the date\n     * @return parsed date\n     */\n    private static LocalDate parseDate(final String date) {\n        int year = Integer.parseInt(date.substring(0, 4)); // year\n        int month = Integer.parseInt(date.substring(4, 6)); // month\n        int day = Integer.parseInt(date.substring(6, 8)); // day\n\n        return LocalDate.of(year, month, day);\n    }\n\n    private static BigDecimal parseAmount(final String amount) {\n\n        /* Must trim the amount for a clean parse\n         * Some banks leave extra spaces before the value\n         */\n\n        try {\n            return new BigDecimal(amount.trim());\n        } catch (final NumberFormatException e) {\n            if (amount.contains(\",\")) { // OFX file in not valid and uses commas for decimal separators\n\n                // Use the French locale as it uses commas for decimal separators\n                DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.FRENCH);\n                df.setParseBigDecimal(true);    // force return value of BigDecimal\n\n                try {\n                    return (BigDecimal) df.parseObject(amount.trim());\n                } catch (final ParseException pe) {\n                    logger.log(Level.INFO, \"Parse amount was: {0}\", amount);\n                    logger.log(Level.SEVERE, e.getLocalizedMessage(), pe);\n                }\n            }\n\n            return BigDecimal.ZERO; // give up at this point\n        }\n    }\n\n    private static boolean parseBoolean(final String bool) {\n        return !bool.isEmpty() && bool.startsWith(\"T\");\n    }\n\n    /**\n     * Parses an InputStream and assumes UTF-8 encoding\n     * <p>\n     * Illegal characters are corrected automatically if found\n     *\n     * @param stream InputStream to parse\n     */\n    public void parse(final InputStream stream) {\n\n        final StringBuilder stringBuilder = new StringBuilder();\n\n        try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, ENCODING))) {\n            String line;\n\n            while ((line = reader.readLine()) != null) {\n                stringBuilder.append(line);\n            }\n\n            parse(sanitize(stringBuilder.toString()), ENCODING);\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.toString(), e);\n        }\n    }\n\n    /**\n     * Parses an InputStream using a specified encoding\n     *\n     * @param stream   InputStream to parse\n     * @param encoding encoding to use\n     */\n    private void parse(final InputStream stream, final String encoding) {\n        logger.entering(OfxV2Parser.class.getName(), \"parse\");\n\n        bank = new OfxBank();\n\n        final XMLInputFactory inputFactory = XMLInputFactory.newInstance();\n        inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);\n        inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);\n\n        try (final InputStream input = new BufferedInputStream(stream)) {\n            XMLStreamReader reader = inputFactory.createXMLStreamReader(input, encoding);\n            readOfx(reader);\n        } catch (IOException | XMLStreamException e) {\n            logger.log(Level.SEVERE, e.toString(), e);\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parse\");\n    }\n\n    private void parseFile(final Path path) {\n\n        try (final InputStream stream = new BufferedInputStream(Files.newInputStream(path))) {\n            parse(stream);\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.toString(), e);\n        }\n    }\n\n    public void parse(final String string, final String encoding) throws UnsupportedEncodingException {\n        parse(new ByteArrayInputStream(string.getBytes(encoding)), encoding);\n    }\n\n    private void readOfx(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"readOfx\");\n\n        while (reader.hasNext()) {\n\n            final int event = reader.next();\n\n            if (event == XMLStreamConstants.START_ELEMENT) {\n                switch (reader.getLocalName()) {\n                    case OFX:   // consume the OFX header here\n                        break;\n                    case SIGNONMSGSRSV1:\n                        parseSignOnMessageSet(reader);\n                        break;\n                    case BANKMSGSRSV1:\n                        parseBankMessageSet(reader);\n                        break;\n                    case CREDITCARDMSGSRSV1:\n                        parseCreditCardMessageSet(reader);\n                        break;\n                    case INVSTMTMSGSRSV1:\n                        parseInvestmentAccountMessageSet(reader);\n                        break;\n                    case SECLISTMSGSRSV1:\n                        parseSecuritesMessageSet(reader);\n                        break;\n                    default:\n                        logger.log(Level.WARNING, \"Unknown message set {0}\", reader.getLocalName());\n                        break;\n                }\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"readOfx\");\n    }\n\n    private void parseInvestmentAccountMessageSet(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseInvestmentAccountMessageSet\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case STATUS:\n                            parseStatementStatus(reader);\n                            break;\n                        case CURDEF:\n                            bank.currency = reader.getElementText();\n                            break;\n                        case INVACCTFROM:\n                            parseAccountInfo(bank, parseAccountInfo(reader));\n                            break;\n                        case INVTRANLIST:\n                            parseInvestmentTransactionList(reader);\n                            break;\n                        case INVSTMTRS:     // consume the statement download\n                            break;\n                        case INVPOSLIST:    // consume the securities position list; TODO: Use for reconcile\n                        case INV401KBAL:    // consume 401k balance aggregate\n                        case INV401K:       // consume 401k account info aggregate\n                        case INVBAL:        // consume the investment account balance; TODO: Use for reconcile\n                        case INVOOLIST:     // consume open orders\n                            consumeElement(reader);\n                            break;\n                        case INVSTMTTRNRS:\n                        case TRNUID:\n                        case DTASOF:    // statement date\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown INVSTMTMSGSRSV1 element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the investment account message set aggregate\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseInvestmentAccountMessageSet\");\n    }\n\n    /**\n     * Parses a BANKMSGSRSV1 element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseBankMessageSet(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseBankMessageSet\");\n\n        final QName parsingElement = reader.getName();\n\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case STATUS:\n                            parseStatementStatus(reader);\n                            break;\n                        case CURDEF:\n                            bank.currency = reader.getElementText();\n                            break;\n                        case LEDGERBAL:\n                            parseLedgerBalance(reader);\n                            break;\n                        case AVAILBAL:\n                            parseAvailableBalance(reader);\n                            break;\n                        case BANKACCTFROM:\n                            parseAccountInfo(bank, parseAccountInfo(reader));\n                            break;\n                        case BANKTRANLIST:\n                            parseBankTransactionList(reader);\n                            break;\n                        case STMTTRNRS: // consume it\n                        case TRNUID:\n                        case STMTRS:\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown BANKMSGSRSV1 element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the bank message set aggregate\");\n                        break;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseBankMessageSet\");\n    }\n\n    /**\n     * Parses a CREDITCARDMSGSRSV1 element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseCreditCardMessageSet(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseCreditCardMessageSet\");\n\n        final QName parsingElement = reader.getName();\n\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case STATUS:\n                            parseStatementStatus(reader);\n                            break;\n                        case CURDEF:\n                            bank.currency = reader.getElementText();\n                            break;\n                        case LEDGERBAL:\n                            parseLedgerBalance(reader);\n                            break;\n                        case AVAILBAL:\n                            parseAvailableBalance(reader);\n                            break;\n                        case CCACCTFROM:\n                            parseAccountInfo(bank, parseAccountInfo(reader));\n                            break;\n                        case BANKTRANLIST:\n                            parseBankTransactionList(reader);\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown CREDITCARDMSGSRSV1 element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the credit card message set aggregate\");\n                        break;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseCreditCardMessageSet\");\n    }\n\n    /**\n     * Parses a SECLISTMSGSRSV1 element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseSecuritesMessageSet(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseSecuritesMessageSet\");\n\n        final QName parsingElement = reader.getName();\n\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    if (SECLIST.equals(reader.getLocalName())) {\n                        parseSecuritiesList(reader);\n                    } else {\n                        logger.log(Level.WARNING, \"Unknown SECLISTMSGSRSV1 element: {0}\", reader.getLocalName());\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the sercurites set\");\n                        break;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseSecuritesMessageSet\");\n    }\n\n    private void parseSecuritiesList(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseSecuritiesList\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case ASSETCLASS:\n                        case OPTTYPE:\n                        case STRIKEPRICE:\n                        case DTEXPIRE:\n                        case SHPERCTRCT:\n                        case YIELD:\n                            break;  // just consume it, not used\n                        case SECID: // underlying stock for an Option.  Not used, so consume it here\n                        case UNIQUEID:\n                        case UNIQUEIDTYPE:\n                            break;\n                        case SECINFO:\n                            parseSecurity(reader);\n                            break;\n                        case MFINFO: // just consume it\n                        case OPTINFO:\n                        case STOCKINFO:\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown SECLIST element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the securities list\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseSecuritiesList\");\n    }\n\n    private void parseSecurity(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseSecurity\");\n\n        final QName parsingElement = reader.getName();\n\n        final ImportSecurity importSecurity = new ImportSecurity();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case FIID:\n                        case SECID: // consume it:\n                            break;\n                        case UNIQUEIDTYPE:\n                            importSecurity.idType = reader.getElementText().trim();\n                            break;\n                        case UNIQUEID:\n                            importSecurity.setId(reader.getElementText().trim());\n                            break;\n                        case SECNAME:\n                            importSecurity.setSecurityName(reader.getElementText().trim());\n                            break;\n                        case TICKER:\n                            importSecurity.setTicker(reader.getElementText().trim());\n                            break;\n                        case UNITPRICE:\n                            importSecurity.setUnitPrice(parseAmount(reader.getElementText()));\n                            break;\n                        case DTASOF:\n                            importSecurity.setLocalDate(parseDate(reader.getElementText()));\n                            break;\n                        case CURRENCY: // consume the currency aggregate for unit price and handle here\n                        case ORIGCURRENCY:\n                            break;\n                        case RATING:    // consume, not used\n                            break;\n                        case CURSYM:\n                            importSecurity.setCurrency(reader.getElementText().trim());\n                            break;\n                        case CURRATE:\n                            importSecurity.setCurrencyRate(parseAmount(reader.getElementText()));\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown SECINFO element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the security info\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        bank.addSecurity(importSecurity);\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseSecurity\");\n    }\n\n    /**\n     * Parses a BANKTRANLIST element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseBankTransactionList(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseBankTransactionList\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case DTSTART:\n                            bank.dateStart = parseDate(reader.getElementText());\n                            break;\n                        case DTEND:\n                            bank.dateEnd = parseDate(reader.getElementText());\n                            break;\n                        case STMTTRN:\n                            parseBankTransaction(reader);\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown BANKTRANLIST element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the bank transaction list\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseBankTransactionList\");\n    }\n\n    private void parseInvestmentTransactionList(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseInvestmentTransactionList\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case DTSTART:\n                            bank.dateStart = parseDate(reader.getElementText());\n                            break;\n                        case DTEND:\n                            bank.dateEnd = parseDate(reader.getElementText());\n                            break;\n                        case BUYSTOCK:\n                        case BUYMF:\n                        case BUYOTHER:\n                        case INCOME:\n                        case REINVEST:\n                        case SELLSTOCK:\n                            parseInvestmentTransaction(reader);\n                            break;\n                        case INVBANKTRAN:\n                            parseBankTransaction(reader);\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown INVTRANLIST element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the investment transaction list\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseInvestmentTransactionList\");\n    }\n\n    private void parseInvestmentTransaction(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseInvestmentTransaction\");\n\n        final QName parsingElement = reader.getName();\n\n        final ImportTransaction tran = new ImportTransaction();\n\n        // set the descriptive transaction type text as well\n        tran.setTransactionTypeDescription(parsingElement.toString());\n\n        // extract the investment transaction type from the element name\n        switch (parsingElement.toString()) {\n            case BUYMF:\n            case BUYOTHER:\n            case BUYSTOCK:\n                tran.setTransactionType(TransactionType.BUYSHARE);\n                break;\n            case SELLMF:\n            case SELLOTHER:\n            case SELLSTOCK:\n                tran.setTransactionType(TransactionType.SELLSHARE);\n                break;\n            case INCOME:    // dividend\n                tran.setTransactionType(TransactionType.DIVIDEND);\n                break;\n            case REINVEST:\n                tran.setTransactionType(TransactionType.REINVESTDIV);\n                break;\n            default:\n                logger.log(Level.WARNING, \"Unknown investment transaction type: {0}\", parsingElement.toString());\n                break;\n        }\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case DTSETTLE:\n                            tran.setDatePosted(parseDate(reader.getElementText()));\n                            break;\n                        case DTTRADE:\n                            tran.setDateUser(parseDate(reader.getElementText()));\n                            break;\n                        case TOTAL: // total of the investment transaction\n                            tran.setAmount(parseAmount(reader.getElementText()));\n                            break;\n                        case FITID:\n                            tran.setFITID(reader.getElementText());\n                            break;\n                        case UNIQUEID:  // the security for the transaction\n                            tran.setSecurityId(reader.getElementText());\n                            break;\n                        case UNIQUEIDTYPE:  // the type of security for the transaction\n                            tran.setSecurityType(reader.getElementText());\n                            break;\n                        case UNITS:\n                            tran.setUnits(parseAmount(reader.getElementText()));\n                            break;\n                        case UNITPRICE:\n                            tran.setUnitPrice(parseAmount(reader.getElementText()));\n                            break;\n                        case FEES:  // investment fees\n                            tran.setFees(parseAmount(reader.getElementText()));\n                            break;\n                        case COMMISSION:    // investment commission\n                            tran.setCommission(parseAmount(reader.getElementText()));\n                            break;\n                        case INCOMETYPE:\n                            tran.setIncomeType(reader.getElementText());\n                            break;\n                        case SUBACCTSEC:\n                        case SUBACCTFROM:\n                        case SUBACCTTO:\n                        case SUBACCTFUND:\n                            tran.setSubAccount(reader.getElementText());\n                            break;\n                        case CHECKNUM:\n                            tran.setCheckNumber(reader.getElementText());\n                            break;\n                        case NAME:\n                        case PAYEE: // either PAYEE or NAME will be used\n                            tran.setPayee(removeExtraWhiteSpace(reader.getElementText()));\n                            break;\n                        case MEMO:\n                            tran.setMemo(removeExtraWhiteSpace(reader.getElementText()));\n                            break;\n                        case CATEGORY:  // Chase bank mucking up the OFX standard\n                            break;\n                        case SIC:\n                            tran.setSIC(reader.getElementText());\n                            break;\n                        case REFNUM:\n                            tran.setRefNum(reader.getElementText());\n                            break;\n                        case PAYEEID:\n                            tran.setPayeeId(removeExtraWhiteSpace(reader.getElementText()));\n                            break;\n                        case CURRENCY:  // currency used for the transaction\n                            //tran.currency = reader.getElementText();\n                            consumeElement(reader);\n                            break;\n                        case ORIGCURRENCY:\n                            tran.setCurrency(reader.getElementText());\n                            break;\n                        case TAXEXEMPT:\n                            tran.setTaxExempt(parseBoolean(reader.getElementText()));\n                            break;\n                        case BUYTYPE:\n                        case INVBUY:    // consume\n                        case INVTRAN:\n                        case SECID:\n                            break;\n                        case LOANID:    // consume 401k loan information\n                        case LOANPRINCIPAL:\n                        case LOANINTEREST:\n                            break;\n                        case INV401KSOURCE: // consume 401k information\n                        case DTPAYROLL:\n                        case PRIORYEARCONTRIB:\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown investment transaction element: {0}\",\n                                    reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        //logger.fine(\"Found the end of the investment transaction\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        bank.addTransaction(tran);\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseInvestmentTransaction\");\n    }\n\n    private String removeExtraWhiteSpace(final String string) {\n        return extraSpaceRegex.matcher(string).replaceAll(\" \").trim();\n    }\n\n    /**\n     * Parses a STMTTRN element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseBankTransaction(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseBankTransaction\");\n\n        final QName parsingElement = reader.getName();\n\n        final ImportTransaction tran = new ImportTransaction();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case TRNTYPE:\n                            tran.setTransactionTypeDescription(reader.getElementText());\n                            break;\n                        case DTPOSTED:\n                            tran.setDatePosted(parseDate(reader.getElementText()));\n                            break;\n                        case DTUSER:\n                            tran.setDateUser(parseDate(reader.getElementText()));\n                            break;\n                        case TRNAMT:\n                            tran.setAmount(parseAmount(reader.getElementText()));\n                            break;\n                        case FITID:\n                            tran.setFITID(reader.getElementText());\n                            break;\n                        case CHECKNUM:\n                            tran.setCheckNumber(reader.getElementText());\n                            break;\n                        case NAME:\n                        case PAYEE: // either PAYEE or NAME will be used\n                            tran.setPayee(removeExtraWhiteSpace(reader.getElementText()));\n                            break;\n                        case MEMO:\n                            tran.setMemo(removeExtraWhiteSpace(reader.getElementText()));\n                            break;\n                        case CATEGORY:  // Chase bank mucking up the OFX standard\n                            break;\n                        case SIC:\n                            tran.setSIC(reader.getElementText());\n                            break;\n                        case REFNUM:\n                            tran.setRefNum(reader.getElementText());\n                            break;\n                        case PAYEEID:\n                            tran.setPayeeId(removeExtraWhiteSpace(reader.getElementText()));\n                            break;\n                        case CURRENCY:\n                        case ORIGCURRENCY:\n                            tran.setCurrency(reader.getElementText());\n                            break;\n                        case SUBACCTFUND: // transfer into / out off an investment account\n                            tran.setSubAccount(reader.getElementText());\n                            break;\n                        case STMTTRN:   // consume, occurs with an investment account transfer\n                            break;\n                        case BANKACCTTO:\n                        case CCACCTTO:\n                        case INVACCTTO:\n                            parseAccountInfo(tran, parseAccountInfo(reader));\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown STMTTRN element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the bank transaction\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        bank.addTransaction(tran);\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseBankTransaction\");\n    }\n\n    private static void parseAccountInfo(final ImportTransaction importTransaction, final AccountInfo accountInfo) {\n        importTransaction.setAccountTo(accountInfo.accountId);\n    }\n\n    /**\n     * Parses a BANKACCTFROM element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private static AccountInfo parseAccountInfo(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseAccountInfo\");\n\n        final QName parsingElement = reader.getName();\n\n        final AccountInfo accountInfo = new AccountInfo();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case BANKID:\n                        case BROKERID:  // normally a URL per the OFX specification\n                            accountInfo.bankId = reader.getElementText();\n                            break;\n                        case ACCTID:\n                            accountInfo.accountId = reader.getElementText();\n                            break;\n                        case ACCTTYPE:\n                            accountInfo.accountType = reader.getElementText();\n                            break;\n                        case BRANCHID:\n                            accountInfo.branchId = reader.getElementText();\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown BANKACCTFROM element: {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the bank and account info aggregate\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseAccountInfo\");\n\n        return accountInfo;\n    }\n\n    private static void parseAccountInfo(final OfxBank ofxBank, final AccountInfo accountInfo) {\n        ofxBank.bankId = accountInfo.bankId;\n        ofxBank.branchId = accountInfo.branchId;\n        ofxBank.accountId = accountInfo.accountId;\n        ofxBank.accountType = accountInfo.accountType;\n    }\n\n    /**\n     * Parses a LEDGERBAL element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseLedgerBalance(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseLedgerBalance\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case BALAMT:\n                            bank.ledgerBalance = parseAmount(reader.getElementText());\n                            break;\n                        case DTASOF:\n                            bank.ledgerBalanceDate = parseDate(reader.getElementText());\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown ledger balance information {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the ledger balance aggregate\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseLedgerBalance\");\n    }\n\n    /**\n     * Parses a AVAILBAL element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseAvailableBalance(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseAvailableBalance\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case BALAMT:\n                            bank.availBalance = parseAmount(reader.getElementText());\n                            break;\n                        case DTASOF:\n                            bank.availBalanceDate = parseDate(reader.getElementText());\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown AVAILBAL element {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the available balance aggregate\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseAvailableBalance\");\n    }\n\n    /**\n     * Parses a SIGNONMSGSRSV1 element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseSignOnMessageSet(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseSignOnMessageSet\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case LANGUAGE:\n                            language = reader.getElementText();\n                            break;\n                        case STATUS:\n                            parseSignOnStatus(reader);\n                            break;\n                        case FI:\n                        case FID:\n                        case ORG:\n                        case INTUBID:\n                        case INTUUSERID:\n                        default:\n                            break;\n                    }\n\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the sign-on message set aggregate\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseSignOnMessageSet\");\n    }\n\n    /**\n     * Parses a STATUS element from the SignOn element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseSignOnStatus(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseSignOnStatus\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case CODE:\n                            try {\n                                statusCode = Integer.parseInt(reader.getElementText());\n                            } catch (final NumberFormatException ex) {\n                                logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n                            }\n                            break;\n                        case SEVERITY:\n                            statusSeverity = reader.getElementText();\n                            break;\n                        case MESSAGE:   // consume it, not used\n                            statusMessage = reader.getElementText();\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown STATUS element {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the statusCode response\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseSignOnStatus\");\n    }\n\n    /**\n     * Parses a STATUS element from the statement element\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private void parseStatementStatus(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"parseSignOnStatus\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    switch (reader.getLocalName()) {\n                        case CODE:\n                            try {\n                                bank.statusCode = Integer.parseInt(reader.getElementText());\n                            } catch (final NumberFormatException ex) {\n                                logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n                            }\n                            break;\n                        case SEVERITY:\n                            bank.statusSeverity = reader.getElementText();\n                            break;\n                        case MESSAGE:\n                            bank.statusMessage = reader.getElementText();\n                            break;\n                        default:\n                            logger.log(Level.WARNING, \"Unknown STATUS element {0}\", reader.getLocalName());\n                            break;\n                    }\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.fine(\"Found the end of the statement status response\");\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"parseStatementStatus\");\n    }\n\n    /**\n     * Consumes an element that will not be used\n     *\n     * @param reader shared XMLStreamReader\n     * @throws XMLStreamException XML parsing error has occurred\n     */\n    private static void consumeElement(final XMLStreamReader reader) throws XMLStreamException {\n        logger.entering(OfxV2Parser.class.getName(), \"consumeElement\");\n\n        final QName parsingElement = reader.getName();\n\n        parse:\n        while (reader.hasNext()) {\n            final int event = reader.next();\n\n            switch (event) {\n                case XMLStreamConstants.START_ELEMENT:\n                    reader.getLocalName();\n\n                    break;\n                case XMLStreamConstants.END_ELEMENT:\n                    if (reader.getName().equals(parsingElement)) {\n                        logger.log(Level.FINEST, \"Found the end of consumed element {0}\", reader.getName());\n                        break parse;\n                    }\n                default:\n            }\n        }\n\n        logger.exiting(OfxV2Parser.class.getName(), \"consumeElement\");\n    }\n\n    public OfxBank getBank() {\n        logger.log(Level.INFO, \"OFX Status was: {0}\", statusCode);\n        logger.log(Level.INFO, \"Status Level was: {0}\", statusSeverity);\n        logger.log(Level.INFO, \"File language was: {0}\", language);\n\n        return bank;\n    }\n\n    int getStatusCode() {\n        return statusCode;\n    }\n\n    String getStatusSeverity() {\n        return statusSeverity;\n    }\n\n    public String getLanguage() {\n        return language;\n    }\n\n    String getStatusMessage() {\n        return statusMessage;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/ofx/Sanitize.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\n/**\n * Utility class for handling OFX files with invalid characters\n *\n * @author Craig Cavanaugh\n */\nclass Sanitize {\n\n    private Sanitize() {\n        // utility class\n    }\n\n\n    /**\n     * Replaces illegal XML characters with escaped characters\n     *\n     * @param xml String to process\n     * @return valid string\n     */\n    static String sanitize(final String xml) {\n        String ugly = xml;\n\n        // remove all XML declarations as they are not needed\n        ugly = ugly.replaceAll(\"<\\\\?xml.*\\\\?>\", \"\");\n\n        ugly = ugly.replaceAll(\"&(?!(?:amp);)\", \"&amp;\");\n        ugly = ugly.replaceAll(\"\\\"\", \"&quot;\");\n        ugly = ugly.replaceAll(\"'\", \"&apos;\");\n\n        return ugly;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifAccount.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport jgnash.convert.importat.DateFormat;\nimport jgnash.convert.importat.ImportBank;\n\n/**\n * @author Craig Cavanaugh\n */\npublic class QifAccount extends ImportBank<QifTransaction> {\n\n    public String name;\n\n    public String type;\n\n    public String description = \"\";\n\n    private DateFormat dateFormat = null;\n\n    @Override\n    public List<QifTransaction> getTransactions() {\n\n        if (dateFormat == null) {\n            setDateFormat(QifTransaction.determineDateFormat(super.getTransactions()));\n        }\n\n        reparseDates(getDateFormat());  // reparse the dates before returning\n\n        return super.getTransactions();\n    }\n\n    public QifTransaction get(final int index) {\n        return super.getTransactions().get(index);\n    }\n\n    @Override\n    public String toString() {\n        return \"Name: \" + name + '\\n' + \"Type: \" + type + '\\n' + \"Description: \" + description + '\\n';\n    }\n\n    public void reparseDates(final DateFormat dateFormat) {\n        Objects.requireNonNull(dateFormat);\n\n        setDateFormat(dateFormat);\n\n        for (final QifTransaction transaction: super.getTransactions()) {\n            transaction.setDatePosted(QifTransaction.parseDate(transaction.oDate, dateFormat));\n        }\n    }\n\n    public DateFormat getDateFormat() {\n        if (dateFormat == null) {\n            return DateFormat.US;\n        }\n\n        return dateFormat;\n    }\n\n    public void setDateFormat(final DateFormat dateFormat) {\n        Objects.requireNonNull(dateFormat);\n\n        this.dateFormat = dateFormat;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifCategory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\n/**\n * @author Craig Cavanaugh\n */\nclass QifCategory {\n\n    public String name;\n\n    public String description;\n\n    public String type;\n\n    public QifCategory() {\n        type = \"E\";\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifImport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport jgnash.convert.importat.DateFormat;\nimport jgnash.convert.importat.ImportUtils;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionFactory;\n\n/**\n * QifImport takes a couple of simple steps to prevent importing a duplicate account. Other than that, duplicate\n * transactions will be imported. At this time, it would require a lot of extra and normally unused data to be stored\n * with each transaction and account to prevent duplication. This import utility is ideal for importing an existing data\n * set, but not for importing monthly bank statements. A more specialized import utility may be useful/required for more\n * advanced in\n *\n * @author Craig Cavanaugh\n */\npublic class QifImport {\n\n    /**\n     * Default for a QIF import\n     */\n    private static final String FITID = \"qif\";\n\n    private QifParser parser;\n\n    private final Engine engine;\n\n    private final HashMap<String, Account> expenseMap = new HashMap<>();\n\n    private final HashMap<String, Account> incomeMap = new HashMap<>();\n\n    private final HashMap<String, Account> accountMap = new HashMap<>();\n\n    private boolean partialImport = false;\n\n    private static final Logger logger = Logger.getLogger(\"qifimport\");\n\n    public QifImport() {\n        engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n    }\n\n    public QifParser getParser() {\n        return parser;\n    }\n\n    public void doFullParse(final File file, final DateFormat dateFormat) throws IOException {\n        if (file != null) {\n            parser = new QifParser(dateFormat);\n            parser.parseFullFile(file);\n            logger.info(\"*** Parsing Complete ***\");\n        }\n    }\n\n    public void doFullImport() {\n        if (parser != null) {\n\n            importCategories();\n            importAccounts();\n\n            logger.info(\"*** Importing Complete ***\");\n        }\n    }\n\n    public boolean doPartialParse(final File file) {\n        if (file != null) {\n            partialImport = true;\n\n            parser = new QifParser(DateFormat.US);\n            return parser.parsePartialFile(file);\n        }\n\n        return false;\n    }\n\n    public void dumpStats() {\n        if (parser != null) {\n            parser.dumpStats();\n        }\n    }\n\n    private void importCategories() {\n        logger.info(\"*** Importing Categories ***\");\n\n        loadCategoryMap(engine.getExpenseAccountList(), expenseMap);\n        loadCategoryMap(engine.getIncomeAccountList(), incomeMap);\n        reduceCategories();\n        addCategories();\n    }\n\n    private void importAccounts() {\n        loadAccountMap();\n        addAccounts();\n    }\n\n    private static void loadCategoryMap(final List<Account> list, final Map<String, Account> map) {\n        if (list != null) { // protect against a failed load on a new account\n            for (Account aList : list) {\n                loadCategoryMap(aList, map);\n            }\n        }\n    }\n\n    private static void loadCategoryMap(final Account acc, final Map<String, Account> map) {\n        String pathName = acc.getPathName();\n        int index = pathName.indexOf(':');\n        if (index != -1) {\n            map.put(pathName.substring(index + 1), acc);\n        }\n    }\n\n    /**\n     * Returns a list of all accounts excluding the rootAccount and IncomeAccounts and ExpenseAccounts\n     *\n     * @return List of bank accounts\n     */\n    private List<Account> getBankAccountList() {\n\n        final List<Account> list = engine.getAccountList();\n\n        return list.stream().filter(a -> a.getAccountType() == AccountType.BANK\n                || a.getAccountType() == AccountType.CASH).collect(Collectors.toList());\n    }\n\n    private void loadAccountMap() {\n        List<Account> list = getBankAccountList();\n        list.forEach(this::loadAccountMap);\n    }\n\n    private void loadAccountMap(final Account acc) {\n        String pathName = acc.getPathName();\n        int index = pathName.indexOf(':');\n        if (index != -1) {\n            accountMap.put(pathName.substring(index + 1), acc);\n        }\n    }\n\n    /**\n     * All of the accounts must be added before the transactions are added because the category (account) must exist\n     * first\n     */\n    private void addAccounts() {\n\n        logger.info(\"*** Importing Accounts ***\");\n\n        List<QifAccount> list = parser.accountList;\n        // add all of the accounts first\n        for (QifAccount qAcc : list) {\n            Account acc;\n            if (!accountMap.containsKey(qAcc.name)) { // add the account if it does not exist\n                acc = generateAccount(qAcc);\n                if (acc != null) {\n                    engine.addAccount(engine.getRootAccount(), acc);\n                    loadAccountMap(acc);\n                }\n            }\n        }\n\n        logger.info(\"*** Importing Transactions ***\");\n\n        // go back and add the transactions;\n        for (QifAccount qAcc : list) {\n            Account acc = accountMap.get(qAcc.name);\n\n            // try and match the closest\n            if (acc == null) {\n                acc = engine.getAccountByName(qAcc.name);\n            }\n\n            // TODO Correct import of investment transactions\n            if (acc != null && acc.getAccountType() != AccountType.INVEST) {\n                addTransactions(qAcc, acc);\n            } else {\n                if (acc != null) {\n                    logger.severe(\"Investment transactions not fully supported\");\n                } else {\n                    logger.log(Level.SEVERE, \"Lost the account: {0}\", qAcc.name);\n                }\n            }\n        }\n    }\n\n    private void addTransactions(final QifAccount qAcc, final Account acc) {\n        if (qAcc.getTransactions().isEmpty()) {\n            return;\n        }\n        List<QifTransaction> list = qAcc.getTransactions();\n        for (QifTransaction aList : list) {\n            Transaction tran;\n\n            /*if (acc.getAccountType().equals(AccountType.INVEST)) {\n            //tran = generateInvestmentTransaction(aList, acc);\n            tran = generateTransaction(aList, acc);\n            } else {\n            tran = generateTransaction(aList, acc);\n            }*/\n\n            tran = generateTransaction(aList, acc);\n\n            if (tran != null) {\n                if (partialImport) {\n                    tran.setFitid(FITID);   // importing a bank statement, flag as imported\n                }\n                engine.addTransaction(tran);\n            } else {\n                logger.warning(\"Null Transaction!\");\n            }\n        }\n    }\n\n    private void addCategories() {\n        List<QifCategory> list = parser.categories;\n        Map<String, Account> map;\n        for (QifCategory cat : list) {\n            Account acc = generateAccount(cat);\n            if (acc.getAccountType() == AccountType.EXPENSE) {\n                map = expenseMap;\n            } else {\n                map = incomeMap;\n            }\n            Account parent = findBestParent(cat, map);\n            engine.addAccount(parent, acc);\n            loadCategoryMap(acc, map);\n        }\n    }\n\n    /**\n     * Returns the Account of the best possible parent account for the supplied QifCategory\n     *\n     * @param cat imported QifCategory to match\n     * @param map cached account map\n     * @return best Account match\n     */\n    private Account findBestParent(final QifCategory cat, final Map<String, Account> map) {\n        int i = cat.name.lastIndexOf(':');\n        if (i != -1) {\n            String pathName = cat.name.substring(0, i);\n            while (true) {\n                if (map.containsKey(pathName)) {\n                    return map.get(pathName);\n                }\n                int j = pathName.lastIndexOf(':');\n                if (j != -1) {\n                    pathName = pathName.substring(0, j);\n                } else {\n                    break;\n                }\n            }\n        }\n        Account parent;\n\n        if (cat.type.equals(\"E\")) {\n            parent = ImportUtils.getRootExpenseAccount();\n        } else {\n            parent = ImportUtils.getRootIncomeAccount();\n        }\n\n        if (parent != null) {\n            return parent;\n        }\n\n        return engine.getRootAccount();\n    }\n\n    /**\n     * Returns the best matching account\n     *\n     * @param category QIF category\n     * @return Best matching account\n     */\n    private Account findBestAccount(final String category) {\n        Account acc = null;\n\n        // nulls can happen and don't search on an empty category\n        if (category != null && !category.isEmpty()) {\n            String name = category;\n\n            if (isAccount(name)) { // account\n                name = category.substring(1, category.length() - 1);\n                logger.log(Level.FINEST, \"Looking for bank account: {0}\", name);\n                acc = accountMap.get(name);\n            }\n\n            if (acc == null) { // income or expense account\n                // strip any category tags\n                name = QifUtils.stripCategoryTags(name);\n\n                acc = expenseMap.get(name);\n                if (acc == null) {\n                    acc = incomeMap.get(name);\n                }\n            }\n\n            if (acc == null) {\n                logger.log(Level.WARNING, \"No account match for: {0}\", name);\n            }\n        }\n        return acc;\n    }\n\n    /**\n     * Determines if the supplied String represents a QIF account\n     *\n     * @param category category string to validate\n     * @return true if this is suppose to be an account\n     */\n    private static boolean isAccount(final String category) {\n        return category.startsWith(\"[\") && category.endsWith(\"]\");\n    }\n\n    /*\n     * Removes duplicate categories from a supplied list\n     */\n    private void reduceCategories() {\n        QifCategory cat;\n        String path;\n        List<QifCategory> list = parser.categories;\n        Iterator<QifCategory> i = list.iterator();\n        while (i.hasNext()) {\n            cat = i.next();\n            path = cat.name;\n            if (cat.type.equals(\"E\") && expenseMap.containsKey(path)) {\n                i.remove();\n            } else if (cat.type.equals(\"I\") && incomeMap.containsKey(path)) {\n                i.remove();\n            }\n        }\n    }\n\n    /*\n     * Creates and returns an Account of the correct type given a QifCategory\n     */\n    private Account generateAccount(final QifCategory cat) {\n        Account account;\n        CurrencyNode defaultCurrency = engine.getDefaultCurrency();\n        if (cat.type.equals(\"E\")) {\n            account = new Account(AccountType.EXPENSE, defaultCurrency);\n            // account.setTaxRelated(cat.taxRelated);\n            // account.setTaxSchedule(cat.taxSchedule);\n        } else {\n            account = new Account(AccountType.INCOME, defaultCurrency);\n            // account.setTaxRelated(cat.taxRelated);\n            // account.setTaxSchedule(cat.taxSchedule);\n        }\n\n        // trim off the leading parent account\n        int index = cat.name.lastIndexOf(':');\n\n        if (index != -1) {\n            account.setName(cat.name.substring(index + 1));\n        } else {\n            account.setName(cat.name);\n        }\n\n        account.setDescription(cat.description);\n        return account;\n    }\n\n    private Account generateAccount(final QifAccount acc) {\n        Account account;\n        CurrencyNode defaultCurrency = engine.getDefaultCurrency();\n\n        switch (acc.type) {\n            case \"Bank\":\n                account = new Account(AccountType.BANK, defaultCurrency);\n                break;\n            case \"CCard\":\n                account = new Account(AccountType.CREDIT, defaultCurrency);\n                break;\n            case \"Cash\":\n                account = new Account(AccountType.CASH, defaultCurrency);\n                break;\n            case \"Invst\":\n            case \"Port\":\n                account = new Account(AccountType.INVEST, defaultCurrency);\n                break;\n            case \"Oth A\":\n                account = new Account(AccountType.ASSET, defaultCurrency);\n                break;\n            case \"Oth L\":\n                account = new Account(AccountType.LIABILITY, defaultCurrency);\n                break;\n            default:\n                logger.log(Level.SEVERE, \"Could not generate an account for:\\n{0}\", acc.toString());\n                return null;\n        }\n        account.setName(acc.name);\n        account.setDescription(acc.description);\n        return account;\n    }\n\n    /**\n     * Generates a transaction\n     * <p>\n     * Notes: If a QifTransaction does not specify an account, then assume it is a single\n     * entry transaction for the supplied Account. The transaction most likely came from a online banking source.\n     *\n     * @param qTran Qif transaction to generate Transaction for\n     * @param acc base Account\n     * @return new Transaction\n     */\n    private Transaction generateTransaction(final QifTransaction qTran, final Account acc) {\n        Objects.requireNonNull(acc);\n\n        boolean reconciled = \"x\".equalsIgnoreCase(qTran.status);\n\n        Transaction tran;\n        Account cAcc;\n        if (qTran.getAccount() != null) {\n            cAcc = qTran.getAccount();\n        } else {\n            cAcc = findBestAccount(qTran.category);\n        }\n\n        if (qTran.hasSplits()) {\n            tran = new Transaction();\n            // create a double entry transaction with splits\n            List<QifSplitTransaction> splits = qTran.splits;\n\n            for (QifSplitTransaction splitTransaction : splits) {\n                TransactionEntry split = generateSplitTransaction(splitTransaction, acc);\n\n                Objects.requireNonNull(split);  // should not be null, throw an exception\n                tran.addTransactionEntry(split);\n            }\n\n            ReconcileManager.reconcileTransaction(acc, tran, reconciled ? ReconciledState.RECONCILED : ReconciledState.NOT_RECONCILED);\n        } else if (acc == cAcc && !qTran.hasSplits() || cAcc == null) {\n            // create single entry transaction without splits\n            tran = TransactionFactory.generateSingleEntryTransaction(acc, qTran.getAmount(), qTran.getDatePosted(), qTran.getMemo(),\n                    qTran.getPayee(), qTran.getCheckNumber());\n\n            ReconcileManager.reconcileTransaction(acc, tran, reconciled ? ReconciledState.RECONCILED : ReconciledState.NOT_RECONCILED);\n        } else if (!qTran.hasSplits()) { // && cAcc != null\n            // create a double entry transaction without splits\n            if (qTran.getAmount().signum() == -1) {\n                tran = TransactionFactory.generateDoubleEntryTransaction(cAcc, acc, qTran.getAmount(), qTran.getDatePosted(),\n                        qTran.getMemo(), qTran.getPayee(), qTran.getCheckNumber());\n            } else {\n                tran = TransactionFactory.generateDoubleEntryTransaction(acc, cAcc, qTran.getAmount(), qTran.getDatePosted(),\n                        qTran.getMemo(), qTran.getPayee(), qTran.getCheckNumber());\n            }\n\n            ReconcileManager.reconcileTransaction(cAcc, tran, reconciled ? ReconciledState.RECONCILED : ReconciledState.NOT_RECONCILED);\n            ReconcileManager.reconcileTransaction(acc, tran, reconciled ? ReconciledState.RECONCILED : ReconciledState.NOT_RECONCILED);\n\n            if (isAccount(qTran.category)) {\n                removeMirrorTransaction(qTran, acc); // remove the mirror transaction\n            }\n        } else {\n            // could not find the account this transaction belongs to\n            logger.log(Level.WARNING, \"Could not create following transaction:\" + \"\\n{0}\", qTran.toString());\n            return null;\n        }\n        tran.setDate(qTran.getDatePosted());\n        tran.setPayee(qTran.getPayee());\n        tran.setNumber(qTran.getCheckNumber());\n\n        return tran;\n    }\n\n    private Account unassignedExpense = null;\n\n    private Account unassignedIncome = null;\n\n    /**\n     * Generates a Transaction given a QifSplitTransaction\n     *\n     * @param qTran split qif transaction to convert\n     * @param acc base Account\n     * @return generated TransactionEntry\n     */\n    private TransactionEntry generateSplitTransaction(final QifSplitTransaction qTran, final Account acc) {\n        TransactionEntry tran = new TransactionEntry();\n\n        Account account = findBestAccount(qTran.category);\n\n        /* Verify that the splits category is not assigned to the parent account.  This is\n         * allowed within Quicken, but violates double entry and is not allowed in jGnash.\n         * Wipe the resulting account and default to the unassigned accounts to maintain\n         * integrity.\n         */\n        if (account == acc) {\n            logger.warning(\"Detected an invalid split transactions entry, correcting problem\");\n            account = null;\n        }\n\n        /* If a valid account is found at this point, then it should have a duplicate\n         * entry in another account that needs to be removed\n         */\n        if (account != null && isAccount(qTran.category)) {\n            removeMirrorSplitTransaction(qTran);\n        }\n\n        if (account == null) { // unassigned split transaction.... fix it with a default\n            if (qTran.amount.signum() == -1) { // need an expense account\n                if (unassignedExpense == null) {\n                    unassignedExpense = new Account(AccountType.EXPENSE, engine.getDefaultCurrency());\n                    unassignedExpense.setName(\"** QIF Import - Unassigned Expense Account\");\n                    unassignedExpense.setDescription(\"Fix transactions and delete this account\");\n                    engine.addAccount(engine.getRootAccount(), unassignedExpense);\n                    logger.info(\"Created an account for unassigned expense account\");\n                }\n                account = unassignedExpense;\n            } else {\n                if (unassignedIncome == null) {\n                    unassignedIncome = new Account(AccountType.INCOME, engine.getDefaultCurrency());\n                    unassignedIncome.setName(\"** QIF Import - Unassigned Income Account\");\n                    unassignedIncome.setDescription(\"Fix transactions and delete this account\");\n                    engine.addAccount(engine.getRootAccount(), unassignedIncome);\n                    logger.info(\"Created an account for unassigned income account\");\n                }\n                account = unassignedIncome;\n            }\n        }\n\n        if (qTran.amount.signum() == -1) {\n            tran.setDebitAccount(acc);\n            tran.setCreditAccount(account);\n        } else {\n            tran.setDebitAccount(account);\n            tran.setCreditAccount(acc);\n        }\n        tran.setAmount(qTran.amount.abs());\n        tran.setMemo(qTran.memo);\n\n        return tran;\n    }\n\n    /* Cannot check against check number and payee because Quicken allows for the\n     * different payees at each side of the transaction and does not include the\n     * check number on both sides.\n     *\n     * The date, amount, and account/category is checked to determine if the match is valid\n     */\n    private void removeMirrorTransaction(final QifTransaction qTran, final Account acc) {\n        String name = qTran.category.substring(1, qTran.category.length() - 1);\n        List<QifAccount> list = parser.accountList;\n\n        for (QifAccount qAcc : list) {\n            if (qAcc.name.equals(name)) {\n                List<QifTransaction> items = qAcc.getTransactions();\n                Iterator<QifTransaction> i = items.iterator();\n                QifTransaction tran;\n                while (i.hasNext()) {\n                    tran = i.next();\n                    if (tran.getAmount().compareTo(qTran.getAmount().negate()) == 0 && tran.getDatePosted().equals(qTran.getDatePosted()) && tran.category.contains(acc.getName())) {\n                        i.remove();\n                        logger.finest(\"Removed mirror transaction\");\n\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    private void removeMirrorSplitTransaction(final QifSplitTransaction qTran) {\n        String name = qTran.category.substring(1, qTran.category.length() - 1);\n        logger.log(Level.FINE, \"Category name is: {0}\", name);\n        List<QifAccount> list = parser.accountList;\n\n        for (QifAccount qAcc : list) {\n            if (qAcc.name.equals(name)) {\n                List<QifTransaction> items = qAcc.getTransactions();\n                Iterator<QifTransaction> i = items.iterator();\n                QifTransaction tran;\n                while (i.hasNext()) {\n                    tran = i.next();\n                    if (tran != null) {\n                        if (tran.getAmount().compareTo(qTran.amount.negate()) == 0 && tran.getMemo().equals(qTran.memo)) {\n                            i.remove();\n                            logger.finest(\"Removed mirror split transaction\");\n                            return;\n                        }\n                    } else { // should not occur anymore\n                        logger.log(Level.SEVERE, \"There was a null QifTransaction in QifAccount: \\n{0}\", qAcc.toString());\n                    }\n                }\n            }\n        }\n\n        // could be a split into a bank account... look a level higher\n        // TODO add check against the base account... should point at each other..\n        // qTran's category same as tran category?\n        for (QifAccount qAcc : list) {\n            if (qAcc.name.equals(name)) {\n                List<QifTransaction> items = qAcc.getTransactions();\n                Iterator<QifTransaction> i = items.iterator();\n                QifTransaction tran;\n                while (i.hasNext()) {\n                    tran = i.next();\n                    // is the match an account and the opposite value and does not have any splits?\n                    // is this a valid method?                                                           \n                    if (tran.getAmount().compareTo(qTran.amount.negate()) == 0 && isAccount(tran.category) && !tran.hasSplits()) {\n                        logger.log(Level.FINE, \"Found a match:\\n{0}\", tran.toString());\n                        i.remove();\n                        return;\n                    }\n                }\n            }\n        }\n\n        logger.log(Level.WARNING, \"Did not find matching mirror:\" + \"\\n{0}\", qTran.toString());\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.convert.importat.DateFormat;\nimport jgnash.util.FileMagic;\nimport jgnash.util.NotNull;\n\n/**\n * The QIF format seems to be very broken. Various applications and services\n * export it differently, some have even extended an already broken format to\n * add even more confusion. To make matters worse, the format has changed over\n * time with no indication of what \"version\" the QIF file is.\n * <p>\n * This parses through the QIF file using brute force, no fancy parser or\n * tricks. The QIF file is broken enough that it's easier to find problems with\n * the parser when it's easy to step through the code.\n * <p>\n * The !Option:AutoSwitch and !Clear:AutoSwitch headers do not appear to be used\n * correctly, even by the \"creator\" of the QIF file format. There seems to be\n * confusion as to it's purpose. The best thing to do is ignore AutoSwitch\n * completely and make an educated guess about the data.\n * <p>\n * I'm not very happy with this code, but I'm not sure there is a clean solution\n * to parsing QIF files\n *\n * @author Craig Cavanaugh\n */\npublic final class QifParser {\n\n    private DateFormat dateFormat = DateFormat.US;\n\n    final ArrayList<QifCategory> categories = new ArrayList<>();\n\n    private final ArrayList<QifClassItem> classes = new ArrayList<>();\n\n    public final ArrayList<QifAccount> accountList = new ArrayList<>();\n\n    private final ArrayList<QifSecurity> securities = new ArrayList<>();\n\n    private static final Logger logger = Logger.getLogger(QifParser.class.getName());\n\n    QifParser(final DateFormat dateFormat) {\n        setDateFormat(dateFormat);\n    }\n\n    public QifAccount getBank() {\n        return accountList.get(0);\n    }\n\n    /**\n     * Tests if the source string starts with the prefix string. Case is\n     * ignored.\n     *\n     * @param source the source String.\n     * @param prefix the prefix String.\n     * @return true, if the source starts with the prefix string.\n     */\n    private static boolean startsWith(final String source, final String prefix) {\n        return prefix.length() <= source.length() && source.regionMatches(true, 0, prefix, 0, prefix.length());\n    }\n\n    private void setDateFormat(@NotNull final DateFormat dateFormat) {\n        Objects.requireNonNull(dateFormat);\n        this.dateFormat = dateFormat;\n    }\n\n    void parseFullFile(final File file) throws IOException {\n        parseFullFile(file.getAbsolutePath());\n    }\n\n    boolean parsePartialFile(final File file) {\n        return parsePartialFile(file.getAbsolutePath());\n    }\n\n    private void parseFullFile(final String fileName) throws IOException {\n\n        boolean accountFound = true;\n\n        final Charset charset = FileMagic.detectCharset(fileName);\n\n        try (final QifReader in = new QifReader(Files.newBufferedReader(Paths.get(fileName), charset))) {\n\n            String line = in.readLine();\n\n            while (line != null) {\n                if (startsWith(line, \"!Type:Class\")) {\n                    parseClassList(in);\n                } else if (startsWith(line, \"!Type:Cat\")) {\n                    parseCategoryList(in);\n                } else if (startsWith(line, \"!Account\")) {\n                    parseAccount(in);\n                } else if (startsWith(line, \"!Type:Memorized\")) {\n                    parseMemorizedTransactions(in);\n                } else if (startsWith(line, \"!Type:Security\")) {\n                    parseSecurity(in);\n                } else if (startsWith(line, \"!Type:Prices\")) {\n                    parsePrice(in);\n                } else if (startsWith(line, \"!Type:Bank\")) { // QIF from an online bank statement... assumes the account is known                  \n                    accountFound = false;\n                    break;\n                } else if (startsWith(line, \"!Type:CCard\")) { // QIF from an online credit card statement\n                    accountFound = false;\n                    break;\n                } else if (startsWith(line, \"!Type:Oth\")) { // QIF from an online credit card statement\n                    accountFound = false;\n                    break;\n                } else if (startsWith(line, \"!Type:Cash\")) { // Partial QIF export\n                    accountFound = false;\n                    break;\n                } else if (startsWith(line, \"!Option:AutoSwitch\")) {\n                    logger.info(\"Consuming !Option:AutoSwitch\");\n                } else if (startsWith(line, \"!Clear:AutoSwitch\")) {\n                    logger.info(\"Consuming !Clear:AutoSwitch\");\n                } else {\n                    System.out.println(\"Error: \" + line);\n                }\n                line = in.readLine();\n            }\n        } catch (final FileNotFoundException e) {\n            logger.log(Level.WARNING, \"Could not find file: {0}\", fileName);\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, null, e);\n        }\n\n        if (!accountFound) {\n            throw new IOException(\"The account was not found\");\n        }\n\n        // re-parse the dates\n        for (final QifAccount account : accountList) {\n            account.reparseDates(QifTransaction.determineDateFormat(account.getTransactions()));\n        }\n    }\n\n    private boolean parsePartialFile(final String fileName) {\n\n        final Charset charset = FileMagic.detectCharset(fileName);\n\n        try (QifReader in = new QifReader(Files.newBufferedReader(Paths.get(fileName), charset))) {\n            String peek = in.peekLine();\n            if (startsWith(peek, \"!Type:\")) {\n                final QifAccount acc = new QifAccount(); // \"unknown\" holding account\n                if (parseAccountTransactions(in, acc)) {\n                    accountList.add(acc);\n\n                    logger.finest(\"*** Added account ***\");\n\n                    // re-parse the dates\n                    acc.reparseDates(QifTransaction.determineDateFormat(acc.getTransactions()));\n\n                    return true; // only look for transactions for one account\n                }\n                System.err.println(\"parseAccountTransactions: error\");\n            }\n\n        } catch (final FileNotFoundException fne) {\n            logger.log(Level.WARNING, \"Could not find file: {0}\", fileName);\n        } catch (final IOException ioe) {\n            logger.log(Level.SEVERE, null, ioe);\n        }\n        return false;\n    }\n\n    private void parseAccount(final QifReader in) throws IOException {\n        logger.entering(this.getClass().getName(), \"parseAccount\", in);\n\n        String line;\n        QifAccount acc = new QifAccount();\n\n        boolean result = false;\n\n        line = in.readLine();\n        while (line != null) {\n            if (line.startsWith(\"N\")) {\n                acc.name = line.substring(1);\n            } else if (line.startsWith(\"T\")) {\n                acc.type = line.substring(1);\n            } else if (line.startsWith(\"D\")) {\n                acc.description = line.substring(1);\n            } else if (line.startsWith(\"L\")) {\n                logger.finest(\"Ignoring credit limit\");\n            } else if (line.startsWith(\"/\")) {\n                logger.finest(\"statement balance date\");\n            } else if (line.startsWith(\"$\")) {\n                logger.finest(\"Ignoring statement balance\");\n            } else if (line.startsWith(\"X\")) {\n                // must be GnuCashToQIF... not sure what it is??? ignore it.\n                logger.warning(\"Ignoring 'X' attribute\");\n            } else if (line.startsWith(\"^\")) {\n                String peek = in.peekLine();\n                if (peek == null) { // end of the file in empty account list\n                    accountList.add(acc);\n                    result = true;\n                    break;\n                }\n                if (startsWith(peek, \"!Account\")) {\n                    // must be in an account list, no transaction data here\n                    accountList.add(acc);\n                    acc = new QifAccount();\n                    in.readLine(); // eat the line since we only peeked at it\n                } else if (startsWith(peek, \"!Type:Memor\")) {\n                    accountList.add(acc);\n                    result = true;\n                    break;\n                } else if (startsWith(peek, \"!Type:Invst\")) { // investment transactions follow\n                    logger.fine(\"Found investment transactions\");\n                    /* Search for a duplicate account generated by the account list and\n                     * use it if possible.  Qif out will output a list of empty accounts\n                     * and then follow up with the same account and it's transactions\n                     */\n                    QifAccount dup = searchForDuplicate(acc);\n                    if (dup != null) {\n                        acc = dup; // trade for the duplicate already existing in the list\n                    }\n\n                    if (parseInvestmentAccountTransactions(in, acc)) {\n                        if (dup == null) {\n                            accountList.add(acc); // only add if not a duplicate\n                        }\n                        logger.finest(\"Added Qif Account\");\n                        result = true;\n                        break; // exit here, the outer loop will catch the next account if it exists\n                    }\n                    acc = new QifAccount();\n                } else if (startsWith(peek, \"!Type:Prices\")) { // security prices, jump out\n                    logger.fine(\"Found commodity price; jump out\");\n                    result = true;\n                    break;\n                } else if (startsWith(peek, \"!Type:\")) {\n                    // must be transactions that follow\n                    logger.fine(\"Found bank transactions\");\n\n                    /* Search for a duplicate account generated by the account list and\n                     * use it if possible.  Qif out will output a list of empty accounts\n                     * and then follow up with the same account and it's transactions\n                     */\n                    QifAccount dup = searchForDuplicate(acc);\n                    if (dup != null) {\n                        acc = dup; // trade for the duplicate already existing in the list\n                    }\n\n                    if (parseAccountTransactions(in, acc)) {\n                        if (dup == null) {\n                            accountList.add(acc); // only add if not a duplicate\n                        }\n                        logger.finest(\"Added Qif Account\");\n                        result = true;\n                        break; // exit here, the outer loop will catch the next account if it exists\n                    }\n                    acc = new QifAccount();\n                } else if (startsWith(peek, \"!Clear:Auto\")) {\n                    in.readLine(); // the broken AutoSwitch.... eat the line\n                    accountList.add(acc);\n                    result = true;\n                    break;\n                } else if (startsWith(peek, \"!\")) {\n                    // something weird, assume in empty account list\n                    accountList.add(acc);\n                    result = true;\n                    break;\n                } else {\n                    // must be in an account list using AutoSwitch\n                    accountList.add(acc);\n                    acc = new QifAccount();\n                }\n            } else {\n                break;\n            }\n\n            line = in.readLine();\n        }\n\n        logger.exiting(this.getClass().getName(), \"parseAccount\", result);\n    }\n\n    private QifAccount searchForDuplicate(final QifAccount acc) {\n        String name = acc.name;\n        String type = acc.type;\n        String description = acc.description; // assume non-null description\n\n        Objects.requireNonNull(description);\n\n        if (name == null || type == null) {\n            logger.log(Level.SEVERE, \"Invalid account: \\n{0}\", acc.toString());\n            return null;\n        }\n\n        /* Investment account types are not consistent in Quicken export.... buggy software.\n         * A Type of \"Invst\" is used as a generic when listing the transactions in the\n         * investment account.\n         * TODO \"Port\" is only one type.... need to discover other types\n         */\n        if (type.equals(\"Invst\") || type.equals(\"Port\")) {\n            for (QifAccount a : accountList) {\n                if (a.name.equals(name) && a.description.equals(description)) {\n                    logger.fine(\"Matched a duplicate account\");\n                    return a;\n                }\n            }\n        } else {\n            for (QifAccount a : accountList) {\n                if (a.name.equals(name) && a.type.equals(type) && a.description.equals(description)) {\n                    logger.fine(\"Matched a duplicate account\");\n                    return a;\n                }\n            }\n        }\n        return null;\n    }\n\n    // TODO strip out investment account transaction checks\n    private static boolean parseAccountTransactions(final QifReader in, final QifAccount acc) {\n\n        String line;\n        QifTransaction tran = new QifTransaction();\n        try {\n            line = in.readLine();\n            while (line != null) {\n                if (startsWith(line, \"!Type:\")) {\n                    if (startsWith(line, \"!Type:Invst\")) {\n                        tran.setTransactionTypeDescription(line.substring(1));\n                        tran.setTransactionTypeDescription(line.substring(1));\n                    } else if (startsWith(line, \"!Type:Memor\")) {\n                        in.reset();\n                        return true;\n                    } else {\n                        tran.setTransactionTypeDescription(line.substring(1));\n                    }\n                } else if (line.startsWith(\"D\")) {\n                    /* Preserve the original unparsed date so that it may be\n                     * reevaluated at a later time. */\n                    tran.oDate = line.substring(1);\n                    //tran.datePosted = QifTransaction.parseDate(tran.oDate, dateFormat);\n                } else if (line.startsWith(\"U\")) {\n                    logger.finest(\"Ignoring U\");\n                } else if (line.startsWith(\"T\")) {\n                    tran.setAmount(QifUtils.parseMoney(line.substring(1)));\n                } else if (line.startsWith(\"C\")) {\n                    tran.status = line.substring(1);\n                } else if (line.startsWith(\"P\")) {\n                    tran.setPayee(line.substring(1));\n                } else if (line.startsWith(\"L\")) {\n                    tran.category = line.substring(1);\n                } else if (line.startsWith(\"N\")) {\n                    tran.setCheckNumber(line.substring(1));\n                } else if (line.startsWith(\"M\")) {\n                    tran.setMemo(line.substring(1));\n                } else if (line.startsWith(\"A\")) {\n                    logger.log(Level.INFO, \"Ignored address line: {0}\", line.substring(1));\n                } else if (line.startsWith(\"I\")) {\n                    tran.price = line.substring(1);\n                } else if (line.startsWith(\"^\")) {\n                    acc.addTransaction(tran);\n                    logger.finest(\"*** Added a Transaction ***\");\n                    tran = new QifTransaction();\n                } else if (startsWith(line, \"!Account\")) {\n                    in.reset();\n                    return true;\n                } else if (startsWith(line, \"!Type:Prices\")) { // fund prices... jump out\n                    in.reset();\n                    return true;\n                } else if (line.charAt(0) == 'S' || line.charAt(0) == 'E' || line.charAt(0) == '$'\n                                   || line.charAt(0) == '%') {\n                    // doing a split transaction\n                    in.reset();\n                    QifSplitTransaction split = parseSplitTransaction(in);\n                    if (split != null) {\n                        tran.addSplit(split);\n                        logger.finest(\"*** Added a Split Transaction ***\");\n                    }\n                } else {\n                    logger.log(Level.SEVERE, \"Unknown field: {0}\", line);\n                }\n                in.mark();\n                line = in.readLine();\n            }\n        } catch (IOException e) {\n            return false;\n        }\n        return true;\n    }\n\n    private boolean parseInvestmentAccountTransactions(final QifReader in, final QifAccount acc) {\n\n        boolean result = true;\n\n        logger.entering(this.getClass().getName(), \"parseInvestmentAccountTransactions\", acc);\n        String line;\n        QifTransaction tran = new QifTransaction();\n        try {\n            line = in.readLine();\n            while (line != null) {\n                if (startsWith(line, \"!Type:Invst\")) { // TODO Bogus check?\n                    tran.setTransactionTypeDescription(line.substring(1));\n                } else if (startsWith(line, \"!Type:Memor\")) {\n                    in.reset();\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Type:Prices\")) { // fund prices... jump out\n                    logger.fine(\"Found commodity prices; jumping out\");\n                    in.reset();\n                    result = true;\n                    break;\n                } else if (line.startsWith(\"D\")) {\n                    /* Preserve the original unparsed date so that it may be\n                     * reevaluated at a later time. */\n                    tran.oDate = line.substring(1);\n                    tran.setDatePosted(QifTransaction.parseDate(tran.oDate, dateFormat));\n                } else if (line.startsWith(\"U\")) {\n                    //tran.U = line.substring(1);\n                    logger.finest(\"Ignoring U\");\n                } else if (line.startsWith(\"T\")) {\n                    tran.setAmount(QifUtils.parseMoney(line.substring(1)));\n                } else if (line.startsWith(\"C\")) {\n                    tran.status = line.substring(1);\n                } else if (line.startsWith(\"P\")) {\n                    tran.setPayee(line.substring(1));\n                } else if (line.startsWith(\"L\")) {\n                    tran.category = line.substring(1);\n                } else if (line.startsWith(\"N\")) { // trans type for inv accounts\n                    tran.setCheckNumber(line.substring(1));\n                } else if (line.startsWith(\"M\")) {\n                    tran.setMemo(line.substring(1));\n                } else if (line.startsWith(\"A\")) {\n                    logger.log(Level.INFO, \"Ignored address line: {0}\", line.substring(1));\n                } else if (line.startsWith(\"Y\")) {\n                    tran.security = line.substring(1);\n                } else if (line.startsWith(\"I\")) {\n                    tran.price = line.substring(1);\n                } else if (line.startsWith(\"Q\")) {\n                    tran.quantity = line.substring(1);\n                } else if (line.charAt(0) == '$') { // must check before split trans checks... Does Quicken allow for split investment transactions?\n                    tran.amountTrans = line.substring(1);\n                } else if (line.startsWith(\"^\")) {\n                    acc.addTransaction(tran);\n                    logger.finest(\"*** Added an investment transaction ***\");\n                    tran = new QifTransaction();\n                } else if (startsWith(line, \"!Account\")) {\n                    in.reset();\n                    result = true;\n                    break;\n                } else if (line.charAt(0) == 'S' || line.charAt(0) == 'E' || line.charAt(0) == '$'\n                                   || line.charAt(0) == '%') {\n                    // doing a split transaction\n                    in.reset();\n                    QifSplitTransaction split = parseSplitTransaction(in);\n                    if (split != null) {\n                        tran.addSplit(split);\n                        logger.fine(\"*** Added a Split Transaction ***\");\n                    }\n                } else {\n                    logger.log(Level.SEVERE, \"Unknown field: {0}\", line);\n                }\n                in.mark();\n                line = in.readLine();\n            }\n        } catch (IOException e) {\n            result = false;\n        }\n\n        logger.exiting(this.getClass().getName(), \"parseInvestmentAccountTransactions\", result);\n        return result;\n    }\n\n    private static QifSplitTransaction parseSplitTransaction(final QifReader in) {\n        boolean category = false;\n        boolean memo = false;\n        boolean amount = false;\n        boolean percentage = false;\n        String line;\n        QifSplitTransaction split = new QifSplitTransaction();\n        try {\n            line = in.readLine();\n            while (line != null) {\n                if (line.startsWith(\"S\")) {\n                    if (category) {\n                        in.reset();\n                        return split;\n                    }\n                    category = true;\n                    split.category = line.substring(1);\n                } else if (line.startsWith(\"E\")) {\n                    if (memo) {\n                        in.reset();\n                        return split;\n                    }\n                    memo = true;\n                    split.memo = line.substring(1);\n                } else if (line.startsWith(\"$\")) {\n                    if (amount) {\n                        in.reset();\n                        return split;\n                    }\n                    amount = true;\n                    split.amount = QifUtils.parseMoney(line.substring(1));\n                } else if (line.startsWith(\"%\")) {\n                    if (percentage) {\n                        in.reset();\n                        return split;\n                    }\n                    percentage = true;\n                    // split.percentage = line.substring(1);\n                } else if (line.startsWith(\"^\")) {\n                    in.reset();\n                    return split;\n                } else {\n                    in.reset();\n                    return null;\n                }\n                in.mark();\n                line = in.readLine();\n            }\n            return null;\n        } catch (IOException e) {\n            return null;\n        }\n    }\n\n    /**\n     * Just eats the memorized transaction data.  Will not try to convert to jGnash entities\n     *\n     * @param in {@code QifReader}\n     */\n    private static void parseMemorizedTransactions(final QifReader in) throws IOException {\n        logger.finest(\"*** Start: parseMemorizedTransactions ***\");\n\n        String line = in.readLine();\n\n        while (line != null) {\n            if (line.startsWith(\"K\")) {\n                // munch until all of them are gone\n                line = in.readLine();\n                if (line != null && line.charAt(0) == '^') {\n                    String peek = in.peekLine();\n                    if (peek == null) {\n                        return;\n                    } else if (!peek.startsWith(\"K\")) {\n                        break;\n                    }\n                }\n\n            } else {\n                in.reset();\n                return;\n            }\n            in.mark();\n            line = in.readLine();\n        }\n    }\n\n    private void parseCategoryList(final QifReader in) throws IOException {\n        boolean result = false;\n\n        QifCategory cat = new QifCategory();\n\n        String line = in.readLine();\n\n        while (line != null) {\n            if (line.startsWith(\"N\")) {\n                cat.name = line.substring(1);\n            } else if (line.startsWith(\"D\")) {\n                cat.description = line.substring(1);\n            } else if (line.startsWith(\"T\")) {\n                logger.finest(\"Ignoring tax related flag\");\n            } else if (line.startsWith(\"I\")) {\n                cat.type = \"I\";\n            } else if (line.startsWith(\"E\")) {\n                cat.type = \"E\";\n            } else if (line.startsWith(\"B\")) {\n                logger.finest(\"Ignoring budget amount\");\n            } else if (line.startsWith(\"R\")) {\n                logger.finest(\"Ignoring tax schedule\");\n            } else if (line.startsWith(\"^\")) { // a complete category item\n                categories.add(cat); // add it to the list\n                cat = new QifCategory(); // start a new one\n                in.mark(); // next line might be end of list\n            } else if (line.startsWith(\"!\")) { // done with category list\n                in.reset(); // give the line back\n                result = true; // a good return\n                break;\n            } else {\n                System.out.println(\"Error: \" + line);\n                result = false;\n            }\n            line = in.readLine();\n        }\n\n        logger.exiting(this.getClass().getName(), \"parseCategoryList\", result);\n    }\n\n    private void parseClassList(final QifReader in) throws IOException {\n\n        QifClassItem classItem = new QifClassItem();\n\n        String line = in.readLine();\n\n        while (line != null) {\n            if (line.startsWith(\"N\")) {\n                classItem.name = line.substring(1);\n            } else if (line.startsWith(\"D\")) {\n                classItem.description = line.substring(1);\n            } else if (line.startsWith(\"^\")) { // end of a class item\n                classes.add(classItem); // add it to the list\n                classItem = new QifClassItem(); // start a new one\n                in.mark(); // next line might be end of the list\n            } else if (line.startsWith(\"!\")) { // done with the class list\n                in.reset(); // give the line back\n                return;\n            } else {\n                throw new IOException(\"Error: \" + line);\n            }\n            line = in.readLine();\n        }\n\n    }\n\n    /**\n     * So far, I haven't see a security as part of a list, but it is supported\n     * just in case there is another \"variation\" of the format\n     *\n     * @param in {@code QifReader}\n     */\n    private void parseSecurity(final QifReader in) throws IOException {\n        boolean result = false;\n\n        QifSecurity sec = new QifSecurity();\n\n        String line = in.readLine();\n\n        while (line != null) {\n            if (line.startsWith(\"N\")) {\n                sec.name = line.substring(1);\n            } else if (line.startsWith(\"D\")) {\n                sec.description = line.substring(1);\n            } else if (line.startsWith(\"T\")) {\n                sec.type = line.substring(1);\n            } else if (line.startsWith(\"S\")) {\n                sec.symbol = line.substring(1);\n            } else if (line.startsWith(\"^\")) {\n                securities.add(sec);\n                sec = new QifSecurity();\n                in.mark();\n            } else if (line.startsWith(\"!\")) {  // end of securities\n                in.reset();\n                result = true;\n                break;\n            } else {\n                System.out.println(\"Error: \" + line);\n                break;\n            }\n            line = in.readLine();\n        }\n\n        logger.exiting(this.getClass().getName(), \"parseSecurity\", result);\n    }\n\n    /**\n     * Price data in QIF file is not very informative.... ignore it for now\n     *\n     * @param in {@code QifReader}\n     */\n    private void parsePrice(final QifReader in) throws IOException {\n        logger.entering(this.getClass().getName(), \"parsePrice\");\n\n        boolean result = false;\n\n        String line = in.readLine();\n\n        while (line != null) {\n            if (line.startsWith(\"^\")) {\n                result = true;\n                break;\n            }\n            line = in.readLine();\n        }\n\n        logger.exiting(this.getClass().getName(), \"parsePrice\", result);\n    }\n\n    void dumpStats() {\n        System.out.println(\"Num Classes :\" + classes.size());\n        System.out.println(\"Num Categories :\" + categories.size());\n        System.out.println(\"Num Securities :\" + securities.size());\n        System.out.println(\"Num Accounts :\" + accountList.size());\n\n        int count = accountList.size();\n        for (int i = 0; i < count; i++) {\n            QifAccount acc = accountList.get(i);\n            System.out.println(\"Account \" + (i + 1) + \" \" + acc.name);\n            int size = acc.getTransactions().size();\n            System.out.println(\"    Num Transactions :\" + size);\n            for (int j = 0; j < size; j++) {\n                QifTransaction tran = acc.getTransactions().get(j);\n                System.out.println(\"        Transaction \" + (j + 1) + \" \" + tran.getPayee());\n                System.out.println(\"            Num Splits :\" + tran.splits.size());\n                for (int k = 0; k < tran.splits.size(); k++) {\n                    System.out.println(\"                Split \" + (k + 1) + \" \" + tran.splits.get(k).memo);\n                }\n            }\n        }\n    }\n\n    static class QifSecurity {\n\n        String name;\n\n        String description;\n\n        String symbol;\n\n        String type;\n\n    }\n\n    static class QifClassItem {\n\n        String name;\n\n        String description;\n\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifReader.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.io.Reader;\n\n/**\n * An extended LineNumberReader to help ease the pain of parsing\n * a QIF file\n *\n * @author Craig Cavanaugh\n */\nclass QifReader extends LineNumberReader {\n\n    private static final boolean debug = false;\n\n    QifReader(Reader in) {\n        super(in, 8192);\n    }\n\n    void mark() throws IOException {\n        super.mark(256);\n    }\n\n    @Override\n    public void reset() throws IOException {\n        super.reset();\n        if (debug) {\n            System.out.println(\"Reset\");\n        }\n    }\n\n    /**\n     * Takes a peek at the next line and eats and empty line if found\n     *\n     * @return next readable line, null if at the end of the file\n     * @throws IOException IO exception\n     */\n    String peekLine() throws IOException {\n        String peek;\n        while (true) {\n            mark();\n            peek = readLine();\n            if (peek != null) {\n                peek = peek.trim();\n                reset();\n                if (peek.isEmpty()) {\n                    readLine(); // eat the empty line\n                    if (debug) {\n                        System.out.println(\"*EMPTY LINE*\");\n                    }\n                } else {\n                    return peek.trim();\n                }\n            } else {\n                return null;\n            }\n        }\n    }\n\n    @Override\n    public String readLine() throws IOException {\n        while (true) {\n            try {\n                String line = super.readLine().trim();\n                if (debug) {\n                    System.out.println(\"Line \" + getLineNumber() + \": \" + line);\n                }\n                if (!line.isEmpty()) {\n                    return line;\n                }\n                if (debug) {\n                    System.out.println(\"*EMPTY LINE*\");\n                }\n            } catch (NullPointerException e) {\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifSplitTransaction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.math.BigDecimal;\n\n/**\n * Class for QIF split transaction import\n * \n * @author Craig Cavanaugh\n */\nclass QifSplitTransaction {\n\n    public String category;\n\n    public String memo = \"\";\n\n    public BigDecimal amount;\n\n    //public String percentage;\n\n    public QifSplitTransaction() {\n        amount = BigDecimal.ZERO;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder buf = new StringBuilder();\n        buf.append(\"Memo: \").append(memo).append('\\n');\n        buf.append(\"Category: \").append(category).append('\\n');\n        if (amount != null) {\n            buf.append(\"Amount:\").append(amount).append('\\n');\n        }\n        return buf.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifTransaction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\nimport jgnash.convert.importat.DateFormat;\nimport jgnash.convert.importat.ImportTransaction;\n\n/**\n * Transaction object for a QIF transaction\n *\n * @author Craig Cavanaugh\n */\npublic class QifTransaction extends ImportTransaction {\n\n    private static final Pattern DATE_DELIMITER_PATTERN = Pattern.compile(\"[/'.-]\");\n\n    /**\n     * Original date before conversion\n     */\n    String oDate;\n\n    String status = null;\n\n    public String category = null;\n\n    String security;\n    String price;\n    String quantity;\n    String amountTrans;\n\n    public final ArrayList<QifSplitTransaction> splits = new ArrayList<>();\n\n    void addSplit(QifSplitTransaction split) {\n        splits.add(split);\n    }\n\n    boolean hasSplits() {\n        return !splits.isEmpty();\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder buf = new StringBuilder();\n        buf.append(\"Payee: \").append(getPayee()).append('\\n');\n        buf.append(\"Memo: \").append(getMemo()).append('\\n');\n        buf.append(\"Category: \").append(category).append('\\n');\n        if (getAmount() != null) {\n            buf.append(\"Amount:\").append(getAmount()).append('\\n');\n        }\n\n        buf.append(\"Date: \").append(getDatePosted()).append('\\n');\n        return buf.toString();\n    }\n\n    static DateFormat determineDateFormat(final Collection<QifTransaction> transactions) {\n        Objects.requireNonNull(transactions);\n\n        DateFormat dateFormat = DateFormat.US;   // US date is assumed\n\n        for (final QifTransaction transaction : transactions) {\n\n            // protect against a transaction missing a date Github issue #30\n            if (transaction.oDate != null) {\n                int zero;\n                int one;\n                //int two;\n\n                final String[] chunks = QifTransaction.DATE_DELIMITER_PATTERN.split(transaction.oDate);\n\n                zero = Integer.parseInt(chunks[0].trim());\n                one = Integer.parseInt(chunks[1].trim());\n                //two = Integer.parseInt(chunks[2].trim());\n\n                if (zero > 12 && one <= 12) {   // must have a EU date format\n                    dateFormat = DateFormat.EU;\n                    break;\n                }\n            }\n        }\n\n        return dateFormat;\n    }\n\n    /**\n     * Converts a string into a data object\n     * <p>\n     * {@code\n     * \"6/21' 1\" -> 6/21/2001\n     * \"6/21'01\" -> 6/21/2001\n     * \"9/18'2001 -> 9/18/2001\n     * \"06/21/2001\" -> \"06/21/01\"\n     * \"3.26.03\" -> German version of quicken format \"03-26-2003\"\n     * MSMoney format \"1.1.2005\"\n     * kmymoney2 20.1.94\n     * European dd/mm/yyyy\n     * 21/2/07 -> 02/21/2007 UK\n     * Quicken 2007 D15/2/07\n     * }``\n     *\n     * @param sDate  String QIF date to parse\n     * @param format String identifier of format to parse\n     * @return Returns parsed date and current date if an error occurs\n     */\n    static LocalDate parseDate(final String sDate, final DateFormat format) throws java.time.DateTimeException {\n\n        int month = 0;\n        int day = 0;\n        int year = 0;\n\n        final String[] chunks = DATE_DELIMITER_PATTERN.split(sDate);\n\n        try {\n            switch (format) {\n                case US:\n                    month = Integer.parseInt(chunks[0].trim());\n                    day = Integer.parseInt(chunks[1].trim());\n                    year = Integer.parseInt(chunks[2].trim());\n                    break;\n                case EU:\n                    day = Integer.parseInt(chunks[0].trim());\n                    month = Integer.parseInt(chunks[1].trim());\n                    year = Integer.parseInt(chunks[2].trim());\n                    break;\n            }\n        } catch (final NumberFormatException e) {\n            Logger.getLogger(QifUtils.class.getName()).severe(e.toString());\n        }\n\n        if (year < 100) {\n            if (year < 29) {\n                year += 2000;\n            } else {\n                year += 1900;\n            }\n        }\n\n        try {\n            return LocalDate.of(year, month, day);\n        } catch (Exception e) {\n            Logger.getLogger(QifUtils.class.getName()).severe(\"Invalid date format specified\");\n            return LocalDate.now();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/java/jgnash/convert/importat/qif/QifUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.qif;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.text.NumberFormat;\nimport java.text.ParseException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.util.FileMagic;\n\n/**\n * Various helper methods for importing QIF files\n *\n * @author Craig Cavanaugh\n * @author Navneet Karnani\n */\npublic class QifUtils {\n\n    private static final Pattern MONEY_PREFIX_PATTERN = Pattern.compile(\"\\\\D\");\n\n    private static final Pattern CATEGORY_DELIMITER_PATTERN = Pattern.compile(\"/\");\n\n    private QifUtils() {\n    }\n\n    static BigDecimal parseMoney(final String money) {\n        String sMoney = money;\n\n        if (sMoney != null) {\n            sMoney = sMoney.trim(); // to be safe\n            try {\n                return new BigDecimal(sMoney);\n            } catch (NumberFormatException e) {\n                /* there must be commas, etc in the number.  Need to look for them\n                 * and remove them first, and then try BigDecimal again.  If that\n                 * fails, then give up and use NumberFormat and scale it down\n                 * */\n\n                String[] split = MONEY_PREFIX_PATTERN.split(sMoney);\n                if (split.length > 2) {\n                    StringBuilder buf = new StringBuilder();\n                    if (sMoney.startsWith(\"-\")) {\n                        buf.append('-');\n                    }\n                    for (int i = 0; i < split.length - 1; i++) {\n                        buf.append(split[i]);\n                    }\n                    buf.append('.');\n                    buf.append(split[split.length - 1]);\n                    try {\n                        return new BigDecimal(buf.toString());\n                    } catch (final NumberFormatException e2) {\n                        Logger l = Logger.getLogger(QifUtils.class.getName());\n                        l.info(\"second parse attempt failed\");\n                        l.info(buf.toString());\n                        l.info(\"falling back to rounding\");\n                    }\n                }\n                NumberFormat formatter = NumberFormat.getNumberInstance();\n                try {\n                    Number num = formatter.parse(sMoney);\n                    BigDecimal bd = BigDecimal.valueOf(num.floatValue());\n                    if (bd.scale() > 6) {\n                        Logger l = Logger.getLogger(QifUtils.class.getName());\n                        l.warning(\"-Warning-\");\n                        l.warning(\"Large scale detected in QifUtils.parseMoney\");\n                        l.warning(\"Truncating scale to 2 places\");\n                        l.warning(bd.toString());\n                        bd = bd.setScale(2, MathConstants.roundingMode);\n                        l.warning(bd.toString());\n                    }\n                    return bd;\n                } catch (ParseException ignored) {\n                    Logger.getLogger(QifUtils.class.getName())\n                            .log(Level.SEVERE, \"poorly formatted number: {0}\", sMoney);\n                }\n            }\n        }\n        return BigDecimal.ZERO;\n    }\n\n    public static boolean isFullFile(final File file) {\n\n        boolean result = false;\n\n        final Charset charset = FileMagic.detectCharset(file.getPath());\n\n        try (final QifReader in = new QifReader(Files.newBufferedReader(file.toPath(), charset))) {\n            String line = in.readLine();\n\n            while (line != null) {\n                if (startsWith(line, \"!Type:Class\")) {\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Type:Cat\")) {\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Account\")) {\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Type:Memorized\")) {\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Type:Security\")) {\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Type:Prices\")) {\n                    result = true;\n                    break;\n                } else if (startsWith(line, \"!Type:Bank\")) { // QIF from an online bank statement... assumes the account is known\n                    break;\n                } else if (startsWith(line, \"!Type:CCard\")) { // QIF from an online credit card statement\n                    break;\n                } else if (startsWith(line, \"!Type:Oth\")) { // QIF from an online credit card statement\n                    break;\n                } else if (startsWith(line, \"!Type:Cash\")) { // Partial QIF export\n                    break;\n                } else if (startsWith(line, \"!Option:AutoSwitch\")) {\n                    Logger.getLogger(QifUtils.class.getName()).fine(\"!Option:AutoSwitch\");\n                } else if (startsWith(line, \"!Clear:AutoSwitch\")) {\n                    Logger.getLogger(QifUtils.class.getName()).fine(\"!Clear:AutoSwitch\");\n                } else {\n                    System.out.println(\"Error: \" + line);\n                    break;\n                }\n                line = in.readLine();\n            }\n            in.close();\n            return result;\n        } catch (FileNotFoundException e) {\n            Logger.getLogger(QifUtils.class.getName()).log(Level.SEVERE, \"Could not find file: {0}\",\n                    file.getAbsolutePath());\n\n            return false;\n        } catch (IOException e) {\n            Logger.getLogger(QifUtils.class.getName()).log(Level.SEVERE, null, e);\n            return false;\n        }\n    }\n\n    /**\n     * Tests if the source string starts with the prefix string. Case is\n     * ignored.\n     *\n     * @param source the source String.\n     * @param prefix the prefix String.\n     * @return true, if the source starts with the prefix string.\n     */\n    private static boolean startsWith(final String source, final String prefix) {\n        return prefix.length() <= source.length() && source.regionMatches(true, 0, prefix, 0, prefix.length());\n    }\n\n    /**\n     * Strip any category tags from the category name... found when parsing\n     * transactions\n     *\n     * @param category string to strip\n     * @return the stripped string\n     */\n    static String stripCategoryTags(final String category) {\n        // Auto:Gas/matrix:Vacation > Auto:Gas\n\n        if (category != null && category.contains(\"/\")) {\n            return CATEGORY_DELIMITER_PATTERN.split(category)[0];\n        }\n\n        return category;\n    }\n}\n"
  },
  {
    "path": "jgnash-convert/src/main/resources/jgnash/convert/scripts/tidy.js",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\nThis scripts normalizes the case of the payee and memo fields\n*/\n\nvar importTransaction;  // place holder for the passed ImportTransaction\n\n/* This will be called first and passed the ImportTransaction */\nfunction acceptTransaction(transaction) {\n    importTransaction = transaction;    // do nothing with it in this script\n}\n\nfunction processMemo(memo) {\n    return capitalizeFirstLetter(memo.toLocaleLowerCase());\n}\n\nfunction processPayee(payee) {\n    return titleCase(payee.toLocaleLowerCase());\n}\n\nfunction getDescription(locale) {\n\n    var Locale = Packages.java.util.Locale;\n\n    switch (locale) {\n        case Locale.ENGLISH:\n            return \"Tidy Memo and Payee fields\";\n        default:\n            return \"Tidy Memo and Payee fields\";\n    }\n}\n\nfunction capitalizeFirstLetter(str) {\n    return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nfunction titleCase(str) {\n    return str.replace(/(^|\\s)[a-z]/g,function(f){return f.toUpperCase();});\n}"
  },
  {
    "path": "jgnash-core/build.gradle.kts",
    "content": "description = \"jGnash Core\"\n\nvar moduleName = \"jgnash.core\"\n\nval commonsCollectionsVersion: String by project\nval commonsCsvVersion: String by project\nval commonsLangVersion: String by project\nval commonsMathVersion: String by project\n\nval slf4jVersion: String by project\nval hibernateVersion: String by project\nval hikariVersion: String by project\nval h2Version: String by project\nval hsqldbVersion: String by project\nval xstreamVersion: String by project\nval nettyVersion: String by project\n\nplugins {\n    `java-library`\n}\n\ndependencies {\n    implementation(project(\":jgnash-resources\"))\n\n    // required for HikariCP, override with modular version\n    implementation(\"org.slf4j:slf4j-api:$slf4jVersion\")\n    implementation(\"org.slf4j:slf4j-jdk14:$slf4jVersion\")\n\n    api(\"org.hibernate:hibernate-entitymanager:$hibernateVersion\")\n    implementation(\"org.hibernate:hibernate-hikaricp:$hibernateVersion\")\n    implementation(\"com.zaxxer:HikariCP:$hikariVersion\")\n\n    implementation(\"com.h2database:h2:$h2Version\")\n    implementation(\"org.hsqldb:hsqldb:$hsqldbVersion\")\n\n    implementation(\"com.thoughtworks.xstream:xstream:$xstreamVersion\") {\n        exclude(module = \"xmlpull\")\n        exclude(module = \"xpp3_min\")\n    }\n\n    implementation(\"com.thoughtworks.xstream:xstream-hibernate:$xstreamVersion\") {\n        exclude(module = \"xmlpull\")\n        exclude(module = \"xpp3_min\")\n    }\n\n    implementation(\"io.netty:netty-codec:$nettyVersion\")\n\n    implementation(\"org.apache.commons:commons-collections4:$commonsCollectionsVersion\")\n    implementation(\"org.apache.commons:commons-csv:$commonsCsvVersion\")\n    implementation(\"org.apache.commons:commons-lang3:$commonsLangVersion\")\n    implementation(\"org.apache.commons:commons-math3:$commonsMathVersion\")\n}\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AbstractInvestmentTransactionEntry.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.ManyToOne;\n\nimport jgnash.util.NotNull;\n\n/**\n * Investment Transaction Entry.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic abstract class AbstractInvestmentTransactionEntry extends TransactionEntry {\n\n    /**\n     * Security for this entry.\n     */\n    @ManyToOne\n    private SecurityNode securityNode;\n\n    /**\n     * share price.\n     */\n    @Column(precision = 26, scale = 8)\n    private BigDecimal price;\n\n    /**\n     * number of shares.\n     */\n    @Column(precision = 26, scale = 8)\n    private BigDecimal quantity;\n\n    /**\n     * Creates a new instance of InvestmentTransactionEntry.\n     */\n    protected AbstractInvestmentTransactionEntry() {\n        setTransactionTag(TransactionTag.INVESTMENT);\n    }\n\n    /**\n     * Calculates the total of the value of the shares, gains, fees, etc. as it\n     * pertains to an account.\n     * <p>\n     * <b>Not intended for use to calculate account balances</b>\n     *\n     * @return total resulting total for this entry\n     * @see InvestmentTransaction#getTotal(jgnash.engine.Account)\n     */\n    public BigDecimal getTotal() {\n        return getQuantity().multiply(getPrice());\n    }\n\n    public SecurityNode getSecurityNode() {\n        return securityNode;\n    }\n\n    void setSecurityNode(final SecurityNode securityNode) {\n        this.securityNode = securityNode;\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    void setPrice(final BigDecimal price) {\n        this.price = price;\n    }\n\n    /**\n     * Assigns the number of shares for this transaction. The value should be\n     * always be a positive value.\n     *\n     * @param quantity the quantity of securities to assign to this account\n     * @see #getSignedQuantity()\n     */\n    void setQuantity(final BigDecimal quantity) {\n        this.quantity = quantity;\n    }\n\n    /**\n     * Returns the number of shares assigned to this transaction.\n     *\n     * @return the quantity of securities for this transaction\n     * @see #getSignedQuantity()\n     */\n    public BigDecimal getQuantity() {\n        return quantity;\n    }\n\n    /**\n     * Returns the number of shares as it would impact the sum of the investment\n     * accounts shares. Useful for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    public abstract BigDecimal getSignedQuantity();\n\n    /**\n     * Returns the type of this transaction entry.\n     *\n     * @return the transaction type\n     */\n    @NotNull public abstract TransactionType getTransactionType();\n\n    @Override\n    public String toString() {\n\n        return super.toString() + \"Security:       \" + getSecurityNode().getSymbol()\n                + System.lineSeparator() + \"Quantity:       \" + getQuantity().toPlainString()\n                + System.lineSeparator() + \"Price:          \" + getPrice().toPlainString()\n                + System.lineSeparator();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/Account.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.FetchType;\nimport javax.persistence.JoinColumn;\nimport javax.persistence.JoinTable;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\nimport javax.persistence.OrderBy;\nimport javax.persistence.PostLoad;\nimport javax.persistence.Transient;\n\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Account object.  The {@code Account} object is mutable.  Changes should be made using the {@code Engine} to\n * ensure correct state and persistence.\n *\n * @author Craig Cavanaugh\n * @author Jeff Prickett prickett@users.sourceforge.net\n */\n@Entity\npublic class Account extends StoredObject implements Comparable<Account> {\n\n    static final int MAX_ATTRIBUTE_LENGTH = 8192;\n\n    /**\n     * Attribute key for the last attempted reconciliation date.\n     */\n    public static final String RECONCILE_LAST_ATTEMPT_DATE = \"Reconcile.LastAttemptDate\";\n\n    /**\n     * Attribute key for the last successful reconciliation date.\n     */\n    public static final String RECONCILE_LAST_SUCCESS_DATE = \"Reconcile.LastSuccessDate\";\n\n    /**\n     * Attribute key for the last reconciliation statement date.\n     */\n    public static final String RECONCILE_LAST_STATEMENT_DATE = \"Reconcile.LastStatementDate\";\n\n    /**\n     * Attribute key for the last reconciliation opening balance.\n     */\n    public static final String RECONCILE_LAST_OPENING_BALANCE = \"Reconcile.LastOpeningBalance\";\n\n    /**\n     * Attribute key for the last reconciliation closing balance.\n     */\n    public static final String RECONCILE_LAST_CLOSING_BALANCE = \"Reconcile.LastClosingBalance\";\n\n    private static final Pattern numberPattern = Pattern.compile(\"\\\\d+\");\n\n    private static final Logger logger = Logger.getLogger(Account.class.getName());\n\n    /**\n     * String delimiter for reported account structure.\n     */\n    private static String accountSeparator = \":\";\n\n    @ManyToOne\n    Account parentAccount;\n\n    /**\n     * List of transactions for this account.\n     */\n    @JoinTable\n    @OrderBy(\"date, number, timestamp\")\n    @ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)\n    final Set<Transaction> transactions = new HashSet<>();\n\n    /**\n     * List of securities if this is an investment account.\n     */\n    @JoinColumn()\n    @OrderBy(\"symbol\")\n    @ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)\n    private final Set<SecurityNode> securities = new HashSet<>();\n\n    @Enumerated(EnumType.STRING)\n    private AccountType accountType;\n\n    private boolean placeHolder = false;\n\n    private boolean locked = false;\n\n    private boolean visible = true;\n\n    private boolean excludedFromBudget = false;\n\n    private String name = \"\";\n\n    private String description = \"\";\n\n    @Column(columnDefinition = \"VARCHAR(8192)\")\n    private String notes = \"\";\n\n    /**\n     * CurrencyNode for this account.\n     */\n    @ManyToOne\n    private CurrencyNode currencyNode;\n\n    /**\n     * Sorted list of child accounts.\n     */\n    @OrderBy(\"name\")\n    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)\n    private final Set<Account> children = new HashSet<>();\n\n    /**\n     * Cached list of sorted transactions that is not persisted. This prevents concurrency issues when using a JPA backend\n     */\n    @Transient\n    private transient List<Transaction> cachedSortedTransactionList;\n\n\n    /**\n     * Cached list of sorted accounts this is not persisted.  This prevents concurrency issues when using a JPA backend\n     */\n    @Transient\n    private transient List<Account> cachedSortedChildren;\n\n    /**\n     * Balance of the account.\n     *\n     * Cached balances cannot be persisted to do nature of JPA\n     */\n    @Transient\n    private transient BigDecimal accountBalance;\n\n    /**\n     * Reconciled balance of the account.\n     *\n     * Cached balances cannot be persisted to do nature of JPA\n     */\n    @Transient\n    private transient BigDecimal reconciledBalance;\n\n    /**\n     * User definable account number.\n     */\n    private String accountNumber = \"\";\n\n    /**\n     * User definable bank id. Useful for OFX import\n     */\n    private String bankId;\n\n    /**\n     * User definable account code.  This will control sort order\n     */\n    @Column(nullable = false, columnDefinition = \"int default 0\")\n    private int accountCode;\n\n    @OneToOne(orphanRemoval = true, cascade = {CascadeType.ALL})\n    private AmortizeObject amortizeObject;\n\n    /**\n     * User definable attributes.\n     */\n    @ElementCollection\n    @Column(columnDefinition = \"varchar(8192)\")\n    private final Map<String, String> attributes = new HashMap<>(); // maps from attribute name to value\n\n    private transient ReadWriteLock transactionLock;\n\n    private transient ReadWriteLock childLock;\n\n    private transient ReadWriteLock securitiesLock;\n\n    private transient ReadWriteLock attributesLock;\n\n    private transient AccountProxy proxy;\n\n    /**\n     * No argument public constructor for reflection purposes.\n     *\n     * <b>Do not use to create account new instance</b>\n     */\n    public Account() {\n        transactionLock = new ReentrantReadWriteLock(true);\n        childLock = new ReentrantReadWriteLock(true);\n        securitiesLock = new ReentrantReadWriteLock(true);\n        attributesLock = new ReentrantReadWriteLock(true);\n\n        // CopyOnWrite is used as an alternative to defensive copies\n        cachedSortedChildren = new ArrayList<>();\n    }\n\n    public Account(@NotNull final AccountType type, @NotNull final CurrencyNode node) {\n        this();\n\n        Objects.requireNonNull(type);\n        Objects.requireNonNull(node);\n\n        setAccountType(type);\n        setCurrencyNode(node);\n    }\n\n    private static String getAccountSeparator() {\n        return accountSeparator;\n    }\n\n    static void setAccountSeparator(final String separator) {\n        accountSeparator = separator;\n    }\n\n    ReadWriteLock getTransactionLock() {\n        return transactionLock;\n    }\n\n    private AccountProxy getProxy() {\n        if (proxy == null) {\n            proxy = getAccountType().getProxy(this);\n        }\n        return proxy;\n    }\n\n    /**\n     * Clear cached account balances so they will be recalculated.\n     */\n    void clearCachedBalances() {\n        accountBalance = null;\n        reconciledBalance = null;\n    }\n\n    /**\n     * Adds account transaction in chronological order.\n     *\n     * @param tran the {@code Transaction} to be added\n     * @return <tt>true</tt> the transaction was added successful <tt>false</tt> the transaction was already attached\n     * to this account\n     */\n    boolean addTransaction(final Transaction tran) {\n        if (placeHolder) {\n            logger.severe(\"Tried to add transaction to a place holder account\");\n            return false;\n        }\n\n        transactionLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (!contains(tran)) {\n\n                transactions.add(tran);\n\n                /* The cached list may already contain the transaction if it has not been initialized yet */\n                if (!getCachedSortedTransactionList().contains(tran)) {\n                    getCachedSortedTransactionList().add(tran);\n                    Collections.sort(getCachedSortedTransactionList());\n                }\n\n                clearCachedBalances();\n\n                result = true;\n            } else {\n                logger.log(Level.SEVERE, \"Account: {0}({1}){2}Already have transaction ID: {3}\", new Object[]{getName(),\n                        hashCode(), System.lineSeparator(), tran.hashCode()});\n            }\n\n            return result;\n        } finally {\n            transactionLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Removes the specified transaction from this account.\n     *\n     * @param tran the {@code Transaction} to be removed\n     * @return {@code true} the transaction removal was successful {@code false} the transaction could not be found\n     * within this account\n     */\n    boolean removeTransaction(final Transaction tran) {\n        transactionLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (contains(tran)) {\n                transactions.remove(tran);\n                getCachedSortedTransactionList().remove(tran);\n                clearCachedBalances();\n\n                result = true;\n            } else {\n                Logger.getLogger(Account.class.toString()).log(Level.SEVERE, \"Account: {0}({1}){2}Did not contain transaction ID: {3}\", new Object[]{getName(), getUuid(), System.lineSeparator(), tran.getUuid()});\n            }\n\n            return result;\n        } finally {\n            transactionLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Determines if the specified transaction is attach to this account.\n     *\n     * @param tran the {@code Transaction} to look for\n     * @return {@code true} the transaction is attached to this account {@code false} the transaction is not attached\n     * to this account\n     */\n    public boolean contains(final Transaction tran) {\n        transactionLock.readLock().lock();\n\n        try {\n            return transactions.contains(tran);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Determine if the supplied account is a child of this account.\n     *\n     * @param account to check\n     * @return true if the supplied account is a child of this account\n     */\n    public boolean contains(final Account account) {\n        childLock.readLock().lock();\n\n        try {\n            return cachedSortedChildren.contains(account);\n        } finally {\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a sorted list of transactions for this account that is unmodifiable.\n     *\n     * @return List of transactions\n     */\n    @NotNull\n    public List<Transaction> getSortedTransactionList() {\n        transactionLock.readLock().lock();\n\n        try {\n            return Collections.unmodifiableList(getCachedSortedTransactionList());\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the transaction at the specified index.\n     *\n     * @param index the index of the transaction to return.\n     * @return the transaction at the specified index.\n     * @throws IndexOutOfBoundsException if the index is out of bounds\n     */\n    @NotNull\n    public Transaction getTransactionAt(final int index) {\n        transactionLock.readLock().lock();\n\n        try {\n            return getCachedSortedTransactionList().get(index);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the number of transactions attached to this account.\n     *\n     * @return the number of transactions attached to this account.\n     */\n    public int getTransactionCount() {\n        transactionLock.readLock().lock();\n\n        try {\n            return transactions.size();\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Searches through the transactions and determines the next largest\n     * transaction number.\n     *\n     * @return The next check number; and empty String if numbers are not found\n     */\n    @NotNull\n    public String getNextTransactionNumber() {\n        transactionLock.readLock().lock();\n\n        try {\n            int number = 0;\n\n            for (final Transaction tran : transactions) {\n                if (numberPattern.matcher(tran.getNumber()).matches()) {\n                    try {\n                        number = Math.max(number, Integer.parseInt(tran.getNumber()));\n                    } catch (NumberFormatException e) {\n                        logger.log(Level.INFO, \"Number regex failed\", e);\n                    }\n                }\n            }\n\n            if (number == 0) {\n                return \"\";\n            }\n\n            return Integer.toString(number + 1);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Add account child account given it's reference.\n     *\n     * @param child The child account to add to this account.\n     * @return {@code true} if the account was added successfully, {@code false} otherwise.\n     */\n    boolean addChild(final Account child) {\n        childLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (!children.contains(child) && child != this) {\n                if (child.setParent(this)) {\n                    children.add(child);\n                    result = true;\n\n                    cachedSortedChildren.add(child);\n                    Collections.sort(cachedSortedChildren);\n                }\n            }\n\n            return result;\n        } finally {\n            childLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Removes account child account. The reference to the parent(this) is left so that the parent can be discovered.\n     *\n     * @param child The child account to remove.\n     * @return {@code true} if the specific account was account child of this account, {@code false} otherwise.\n     */\n    boolean removeChild(final Account child) {\n        childLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (children.remove(child)) {\n                result = true;\n\n                cachedSortedChildren.remove(child);\n            }\n            return result;\n        } finally {\n            childLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a sorted list of the children.  A protective copy is returned to protect against concurrency issues.\n     *\n     * @return List of children\n     */\n    public List<Account> getChildren() {\n        childLock.readLock().lock();\n\n        try {\n            return new ArrayList<>(cachedSortedChildren);\n        } finally {\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a sorted list of the children.  A protective copy is returned to protect against concurrency issues.\n     *\n     * @param comparator {@code Comparator} to use\n     * @return List of children\n     */\n    public List<Account> getChildren(final Comparator<? super Account> comparator) {\n        List<Account> accountChildren = getChildren();\n        accountChildren.sort(comparator);\n\n        return accountChildren;\n    }\n\n    /**\n     * Returns the index of the specified {@code Transaction} within this {@code Account}.\n     *\n     * @param tran the {@code Transaction} to look for\n     * @return The index of the {@code Transaction}, -1 if this\n     * {@code Account} does not contain the {@code Transaction}.\n     */\n    public int indexOf(final Transaction tran) {\n        transactionLock.readLock().lock();\n\n        try {\n            return getCachedSortedTransactionList().indexOf(tran);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the number of children this account has.\n     *\n     * @return the number of children this account has.\n     */\n    public int getChildCount() {\n        childLock.readLock().lock();\n\n        try {\n            return cachedSortedChildren.size();\n        } finally {\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the parent account.\n     *\n     * @return the parent of this account, null is this account is not account child\n     */\n    public Account getParent() {\n        childLock.readLock().lock();\n\n        try {\n            return parentAccount;\n        } finally {\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Sets the parent of this {@code Account}.\n     *\n     * @param account The new parent {@code Account}\n     * @return {@code true} is successful\n     */\n    public boolean setParent(final Account account) {\n        childLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (account != this) {\n                parentAccount = account;\n                result = true;\n            }\n\n            return result;\n        } finally {\n            childLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Determines is this {@code Account} has any child{@code Account}.\n     *\n     * @return {@code true} is this {@code Account} has children, {@code false} otherwise.\n     */\n    public boolean isParent() {\n        childLock.readLock().lock();\n\n        try {\n            return !cachedSortedChildren.isEmpty();\n        } finally {\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * The account balance is cached to improve performance and reduce thrashing\n     * of the GC system. The accountBalance is reset when transactions are added\n     * and removed and lazily recalculated.\n     *\n     * @return the balance of this account\n     */\n    public BigDecimal getBalance() {\n        transactionLock.readLock().lock();\n\n        try {\n            if (accountBalance != null) {\n                return accountBalance;\n            }\n            return accountBalance = getProxy().getBalance();\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * The account balance is cached to improve performance and reduce thrashing\n     * of the GC system. The accountBalance is rest when transactions are added\n     * and removed and lazily recalculated.\n     *\n     * @param node CurrencyNode to get balance against\n     * @return the balance of this account\n     */\n    private BigDecimal getBalance(final CurrencyNode node) {\n        transactionLock.readLock().lock();\n\n        try {\n            return adjustForExchangeRate(getBalance(), node);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Get the account balance up to the specified index using the natural\n     * transaction sort order.\n     *\n     * @param index the balance of this account at the specified index.\n     * @return the balance of this account at the specified index.\n     */\n    private BigDecimal getBalanceAt(final int index) {\n        transactionLock.readLock().lock();\n\n        try {\n            return getProxy().getBalanceAt(index);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Get the account balance up to the specified transaction using the natural\n     * transaction sort order.\n     *\n     * @param transaction reference transaction for running balance.  Must be contained within the account\n     * @return the balance of this account at the specified transaction\n     */\n    public BigDecimal getBalanceAt(final Transaction transaction) {\n        transactionLock.readLock().lock();\n\n        try {\n            final int index = indexOf(transaction);\n\n            if (index >= 0) {\n                return getBalanceAt(index);\n            }\n\n            return BigDecimal.ZERO;\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * The reconciled balance is cached to improve performance and reduce\n     * thrashing of the GC system. The reconciledBalance is reset when\n     * transactions are added and removed and lazily recalculated.\n     *\n     * @return the reconciled balance of this account\n     */\n    public BigDecimal getReconciledBalance() {\n        transactionLock.readLock().lock();\n\n        try {\n            if (reconciledBalance != null) {\n                return reconciledBalance;\n            }\n\n            return reconciledBalance = getProxy().getReconciledBalance();\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    private BigDecimal getReconciledBalance(final CurrencyNode node) {\n        return adjustForExchangeRate(getReconciledBalance(), node);\n    }\n\n    private BigDecimal adjustForExchangeRate(final BigDecimal amount, final CurrencyNode node) {\n        if (node.equals(getCurrencyNode())) { // child has the same commodity type\n            return amount;\n        }\n\n        // the account has a different currency, use the last known exchange rate\n        return amount.multiply(getCurrencyNode().getExchangeRate(node));\n    }\n\n    /**\n     * Returns the date of the first unreconciled transaction.\n     *\n     * @return Date of first unreconciled transaction\n     */\n    public LocalDate getFirstUnreconciledTransactionDate() {\n        transactionLock.readLock().lock();\n\n        try {\n            LocalDate date = null;\n\n            for (final Transaction transaction : getSortedTransactionList()) {\n                if (transaction.getReconciled(this) != ReconciledState.RECONCILED) {\n                    date = transaction.getLocalDate();\n                    break;\n                }\n            }\n\n            if (date == null) {\n                date = getCachedSortedTransactionList().get(getTransactionCount() - 1).getLocalDate();\n            }\n\n            return date;\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Get the default opening balance for reconciling the account.\n     *\n     * @return Opening balance for reconciling the account\n     * @see AccountProxy#getOpeningBalanceForReconcile()\n     */\n    public BigDecimal getOpeningBalanceForReconcile() {\n        return getProxy().getOpeningBalanceForReconcile();\n    }\n\n    /**\n     * Returns the balance of the account plus any child accounts.\n     *\n     * @return the balance of this account including the balance of any child\n     * accounts.\n     */\n    public BigDecimal getTreeBalance() {\n        transactionLock.readLock().lock();\n        childLock.readLock().lock();\n\n        try {\n            BigDecimal balance = getBalance();\n\n            for (final Account child : cachedSortedChildren) {\n                balance = balance.add(child.getTreeBalance(getCurrencyNode()));\n            }\n\n            return balance;\n        } finally {\n            transactionLock.readLock().unlock();\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the balance of the account plus any child accounts.\n     *\n     * @param endDate The inclusive end date\n     * @param node The commodity to convert balance to\n     *\n     * @return the balance of this account including the balance of any child\n     * accounts.\n     */\n    public BigDecimal getTreeBalance(final LocalDate endDate, final CurrencyNode node) {\n        transactionLock.readLock().lock();\n        childLock.readLock().lock();\n\n        try {\n            BigDecimal balance = getBalance(endDate, node);\n\n            for (final Account child : cachedSortedChildren) {\n                balance = balance.add(child.getTreeBalance(endDate, node));\n            }\n\n            return balance;\n        } finally {\n            transactionLock.readLock().unlock();\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the balance of the account plus any child accounts. The balance\n     * is adjusted to the current exchange rate of the supplied commodity if\n     * needed.\n     *\n     * @param node The commodity to convert balance to\n     * @return the balance of this account including the balance of any child\n     * accounts.\n     */\n    private BigDecimal getTreeBalance(final CurrencyNode node) {\n        transactionLock.readLock().lock();\n        childLock.readLock().lock();\n\n        try {\n            BigDecimal balance = getBalance(node);\n\n            for (final Account child : cachedSortedChildren) {\n                balance = balance.add(child.getTreeBalance(node));\n            }\n            return balance;\n        } finally {\n            transactionLock.readLock().unlock();\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the reconciled balance of the account plus any child accounts.\n     * The balance is adjusted to the current exchange rate of the supplied\n     * commodity if needed.\n     *\n     * @param node The commodity to convert balance to\n     * @return the balance of this account including the balance of any child\n     * accounts.\n     */\n    private BigDecimal getReconciledTreeBalance(final CurrencyNode node) {\n        transactionLock.readLock().lock();\n        childLock.readLock().lock();\n\n        try {\n            BigDecimal balance = getReconciledBalance(node);\n\n            for (final Account child : cachedSortedChildren) {\n                balance = balance.add(child.getReconciledTreeBalance(node));\n            }\n            return balance;\n        } finally {\n            transactionLock.readLock().unlock();\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the reconciled balance of the account plus any child accounts.\n     *\n     * @return the balance of this account including the balance of any child\n     * accounts.\n     */\n    public BigDecimal getReconciledTreeBalance() {\n        transactionLock.readLock().lock();\n        childLock.readLock().lock();\n\n        try {\n            BigDecimal balance = getReconciledBalance();\n\n            for (final Account child : cachedSortedChildren) {\n                balance = balance.add(child.getReconciledTreeBalance(getCurrencyNode()));\n            }\n            return balance;\n        } finally {\n            transactionLock.readLock().unlock();\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the balance of the transactions inclusive of the start and end\n     * dates.\n     *\n     * @param start The inclusive start date\n     * @param end   The inclusive end date\n     * @return The ending balance\n     */\n    public BigDecimal getBalance(final LocalDate start, final LocalDate end) {\n        Objects.requireNonNull(start);\n        Objects.requireNonNull(end);\n\n        transactionLock.readLock().lock();\n\n        try {\n            return getProxy().getBalance(start, end);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the account balance up to and inclusive of the supplied date. The\n     * returned balance is converted to the specified commodity.\n     *\n     * @param startDate start date\n     * @param endDate   end date\n     * @param node      The commodity to convert balance to\n     * @return the account balance\n     */\n    public BigDecimal getBalance(final LocalDate startDate, final LocalDate endDate, final CurrencyNode node) {\n        transactionLock.readLock().lock();\n\n        try {\n            return adjustForExchangeRate(getBalance(startDate, endDate), node);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the full inclusive ancestry of this\n     * {@code Account}.\n     *\n     * @return {@code List} of accounts\n     */\n    public List<Account> getAncestors() {\n        List<Account> list = new ArrayList<>();\n        list.add(this);\n\n        Account parent = getParent();\n\n        while (parent != null) {\n            list.add(parent);\n            parent = parent.getParent();\n        }\n\n        return list;\n    }\n\n    /**\n     * Returns the the balance of the account plus any child accounts inclusive\n     * of the start and end dates.\n     *\n     * @param start start date\n     * @param end   end date\n     * @param node  CurrencyNode to use for balance\n     * @return account balance\n     */\n    public BigDecimal getTreeBalance(final LocalDate start, final LocalDate end, final CurrencyNode node) {\n        Objects.requireNonNull(start);\n        Objects.requireNonNull(end);\n\n        transactionLock.readLock().lock();\n        childLock.readLock().lock();\n\n        try {\n            BigDecimal returnValue = getBalance(start, end, node);\n\n            for (final Account child : cachedSortedChildren) {\n                returnValue = returnValue.add(child.getTreeBalance(start, end, node));\n            }\n            return returnValue;\n        } finally {\n            transactionLock.readLock().unlock();\n            childLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the account balance up to and inclusive of the supplied localDate.\n     *\n     * @param localDate The inclusive ending localDate\n     * @return The ending balance\n     */\n    public BigDecimal getBalance(final LocalDate localDate) {\n        transactionLock.readLock().lock();\n\n        try {\n            return getProxy().getBalance(localDate);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the account balance up to and inclusive of the supplied date. The\n     * returned balance is converted to the specified commodity.\n     *\n     * @param node The commodity to convert balance to\n     * @param date The inclusive ending date\n     * @return The ending balance\n     */\n    public BigDecimal getBalance(final LocalDate date, final CurrencyNode node) {\n        transactionLock.readLock().lock();\n\n        try {\n            return adjustForExchangeRate(getBalance(date), node);\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a {@code List} of {@code Transaction} that occur during the specified period.\n     * The specified dates are inclusive.\n     *\n     * @param startDate starting date\n     * @param endDate   ending date\n     * @return a {@code List} of transactions that occurred within the specified dates\n     */\n    public List<Transaction> getTransactions(final LocalDate startDate, final LocalDate endDate) {\n        transactionLock.readLock().lock();\n\n        try {\n            return transactions.parallelStream().filter(transaction\n                    -> DateUtils.after(transaction.getLocalDate(), startDate)\n                    && DateUtils.before(transaction.getLocalDate(), endDate)).sorted().collect(Collectors.toList());\n        } finally {\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the commodity node for this account\n     *\n     * Note: method may not be final for Hibernate.\n     *\n     * @return the commodity node for this account.\n     */\n    public CurrencyNode getCurrencyNode() {\n        return currencyNode;\n    }\n\n    /**\n     * Sets the commodity node for this account.\n     *\n     * Note: method may not be final for Hibernate\n     *\n     * @param node The new commodity node for this account.\n     */\n    void setCurrencyNode(@NotNull final CurrencyNode node) {\n        Objects.requireNonNull(node);\n\n        if (!node.equals(currencyNode)) {\n            currencyNode = node;\n\n            clearCachedBalances();  // cached balances will need to be recalculated\n        }\n    }\n\n    public boolean isLocked() {\n        return locked;\n    }\n\n    public void setLocked(final boolean locked) {\n        this.locked = locked;\n    }\n\n    public boolean isPlaceHolder() {\n        return placeHolder;\n    }\n\n    public void setPlaceHolder(final boolean placeHolder) {\n        this.placeHolder = placeHolder;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String desc) {\n        description = desc;\n    }\n\n    public synchronized String getName() {\n        return name;\n    }\n\n    public synchronized void setName(final String newName) {\n        if (!newName.equals(name)) {\n            name = newName;\n        }\n    }\n\n    public synchronized String getPathName() {\n        final Account parent = getParent();\n\n        if (parent != null && parent.getAccountType() != AccountType.ROOT) {\n            return parent.getPathName() + getAccountSeparator() + getName();\n        }\n\n        return getName(); // this account is at the root level\n    }\n\n    public AccountType getAccountType() {\n        return accountType;\n    }\n\n    /**\n     * Sets the account type\n     *\n     * Note: method may not be final for Hibernate\n     *\n     * @param type new account type\n     */\n    void setAccountType(final AccountType type) {\n        Objects.requireNonNull(type);\n\n        if (accountType != null && !accountType.isMutable()) {\n            throw new EngineException(\"Immutable account type\");\n        }\n\n        accountType = type;\n\n        proxy = null; // proxy will need to change\n    }\n\n    /**\n     * Returns the visibility of the account.\n     *\n     * @return boolean is this account is visible, false otherwise\n     */\n    public boolean isVisible() {\n        return visible;\n    }\n\n    /**\n     * Changes the visibility of the account.\n     *\n     * @param visible the new account visibility\n     */\n    public void setVisible(final boolean visible) {\n        this.visible = visible;\n    }\n\n    /**\n     * Returns the notes for this account.\n     *\n     * @return the notes for this account\n     */\n    public String getNotes() {\n        return notes;\n    }\n\n    /**\n     * Sets the notes for this account.\n     *\n     * @param notes the notes for this account\n     */\n    public void setNotes(final String notes) {\n        this.notes = notes;\n    }\n\n    /**\n     * Compares two Account for ordering.  Returned sort order is consistent with JPA order.\n     * The account name, and then account UUID is used1\n     *\n     * @param acc the {@code Account} to be compared.\n     * @return the value {@code 0} if the argument Account is equal to this Account; account\n     * value less than {@code 0} if this Account is before the Account argument; and\n     * account value greater than {@code 0} if this Account is after the Account argument.\n     */\n    @Override\n    public int compareTo(@NotNull final Account acc) {\n\n        // Sort by name\n        int result = getName().compareToIgnoreCase(acc.getName());\n        if (result != 0) {\n            return result;\n        }\n\n        // Sort of uuid after everything else fails.\n        return getUuid().compareTo(acc.getUuid());\n    }\n\n    @Override\n    public String toString() {\n        return name;\n    }\n\n    @Override\n    public boolean equals(final Object other) {\n        return this == other || other instanceof Account && getUuid().equals(((Account) other).getUuid());\n    }\n\n    /**\n     * User definable account code.  This can be used to manage sort order\n     * \n     * @return the user defined account code\n     */\n    public int getAccountCode() {\n        return accountCode;\n    }\n\n    public void setAccountCode(final int accountCode) {\n        this.accountCode = accountCode;\n    }\n\n    /**\n     * Returns the account number. A non-null value is guaranteed\n     *\n     * @return the account number\n     */\n    public String getAccountNumber() {\n        return accountNumber;\n    }\n\n    public void setAccountNumber(final String account) {\n        accountNumber = account;\n    }\n\n    /**\n     * Adds account commodity to the list and ensures duplicates are not added.\n     * The list is sorted according to numeric code\n     *\n     * @param node SecurityNode to add\n     * @return true if successful\n     */\n    boolean addSecurity(final SecurityNode node) {\n        boolean result = false;\n\n        if (node != null && memberOf(AccountGroup.INVEST) && !containsSecurity(node)) {\n            securities.add(node);\n\n            result = true;\n        }\n\n        return result;\n    }\n\n    /**\n     * Removes a {@code SecurityNode} from the account.  If the {@code SecurityNode} is in use by transactions,\n     * removal will be prohibited.\n     *\n     * @param node {@code SecurityNode} to remove\n     * @return {@code true} if successful, {@code false} if used by a transaction or not an active {@code SecurityNode}\n     */\n    boolean removeSecurity(final SecurityNode node) {\n        securitiesLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (!getUsedSecurities().contains(node) && containsSecurity(node)) {\n                securities.remove(node);\n                result = true;\n            }\n\n            return result;\n        } finally {\n            securitiesLock.writeLock().unlock();\n        }\n    }\n\n    public boolean containsSecurity(final SecurityNode node) {\n        securitiesLock.readLock().lock();\n\n        try {\n            return securities.contains(node);\n        } finally {\n            securitiesLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the market value of this account.\n     *\n     * @return market value of the account\n     */\n    public BigDecimal getMarketValue() {\n        return getProxy().getMarketValue();\n    }\n\n    /**\n     * Returns a defensive copy of the security set.\n     *\n     * @return a sorted set\n     */\n    public Set<SecurityNode> getSecurities() {\n        securitiesLock.readLock().lock();\n\n        try {\n            return new TreeSet<>(securities);\n        } finally {\n            securitiesLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a set of used SecurityNodes.\n     *\n     * @return a set of used SecurityNodes\n     */\n    public Set<SecurityNode> getUsedSecurities() {\n        transactionLock.readLock().lock();\n        securitiesLock.readLock().lock();\n\n        try {\n            return transactions.parallelStream().filter(t -> t instanceof InvestmentTransaction).map(t ->\n                    ((InvestmentTransaction) t).getSecurityNode()).collect(Collectors.toCollection(TreeSet::new));\n        } finally {\n            securitiesLock.readLock().unlock();\n            transactionLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the cash balance of this account.\n     *\n     * @return Cash balance of the account\n     */\n    public BigDecimal getCashBalance() {\n        Lock l = transactionLock.readLock();\n        l.lock();\n\n        try {\n            return getProxy().getCashBalance();\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns the depth of the account relative to the {@code RootAccount}.\n     *\n     * @return depth relative to the root\n     */\n    public int getDepth() {\n        int depth = 0;\n\n        Account parent = getParent();\n\n        while (parent != null) {\n            depth++;\n            parent = parent.getParent();\n        }\n\n        return depth;\n    }\n\n    /**\n     * Shortcut method to check account type.\n     *\n     * @param type AccountType to compare against\n     * @return true if supplied AccountType match\n     */\n    public final boolean instanceOf(final AccountType type) {\n        return getAccountType() == type;\n    }\n\n    /**\n     * Shortcut method to check account group membership.\n     *\n     * @param group AccountGroup to compare against\n     * @return true if this account belongs to the supplied group\n     */\n    public final boolean memberOf(final AccountGroup group) {\n        return getAccountType().getAccountGroup() == group;\n    }\n\n    public String getBankId() {\n        return bankId;\n    }\n\n    public void setBankId(final String bankId) {\n        this.bankId = bankId;\n    }\n\n    public boolean isExcludedFromBudget() {\n        return excludedFromBudget;\n    }\n\n    public void setExcludedFromBudget(boolean excludeFromBudget) {\n        this.excludedFromBudget = excludeFromBudget;\n    }\n\n    /**\n     * Amortization object for loan payments.\n     *\n     * @return {@code AmortizeObject} if not null\n     */\n    @Nullable\n    public AmortizeObject getAmortizeObject() {\n        return amortizeObject;\n    }\n\n    void setAmortizeObject(final AmortizeObject amortizeObject) {\n        this.amortizeObject = amortizeObject;\n    }\n\n    /**\n     * Sets an attribute for the {@code Account}.\n     *\n     * @param key   the attribute key\n     * @param value the value. If null, the attribute will be removed\n     */\n    void setAttribute(@NotNull final String key, @Nullable final String value) {\n        attributesLock.writeLock().lock();\n\n        try {\n            if (key.isEmpty()) {\n                throw new EngineException(\"Attribute key may not be empty or null\");\n            }\n\n            if (value == null) {\n                attributes.remove(key);\n            } else {\n                attributes.put(key, value);\n            }\n        } finally {\n            attributesLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Returns an {@code Account} attribute.\n     *\n     * @param key the attribute key\n     * @return the attribute if found\n     * @see Engine#setAccountAttribute\n     */\n    @Nullable\n    String getAttribute(@NotNull final String key) {\n        attributesLock.readLock().lock();\n\n        try {\n            if (key.isEmpty()) {\n                throw new EngineException(\"Attribute key may not be empty or null\");\n            }\n\n            return attributes.get(key);\n        } finally {\n            attributesLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Provides access to a cached and sorted list of transactions. Direct access to the list\n     * is for internal use only.\n     *\n     * @return List of sorted transactions\n     * @see #getSortedTransactionList\n     */\n    private List<Transaction> getCachedSortedTransactionList() {\n\n        // Lazy initialization\n        if (cachedSortedTransactionList == null) {\n            cachedSortedTransactionList = new ArrayList<>(transactions);\n            Collections.sort(cachedSortedTransactionList);\n        }\n\n        return cachedSortedTransactionList;\n    }\n\n    /**\n     * Required by XStream for proper initialization.\n     *\n     * @return Properly initialized Account\n     */\n    protected Object readResolve() {\n        postLoad();\n        return this;\n    }\n\n    @PostLoad\n    private void postLoad() {\n        transactionLock = new ReentrantReadWriteLock(true);\n        childLock = new ReentrantReadWriteLock(true);\n        securitiesLock = new ReentrantReadWriteLock(true);\n        attributesLock = new ReentrantReadWriteLock(true);\n\n        cachedSortedChildren = new ArrayList<>(children);\n        Collections.sort(cachedSortedChildren); // JPA will be naturally sorted, but XML files will not\n    }\n\n    /**\n     * Accounts should not be cloned.\n     *\n     * @return will result in a CloneNotSupportedException\n     * @throws java.lang.CloneNotSupportedException will always occur\n     */\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n        super.clone();\n        throw new CloneNotSupportedException(\"Accounts may not be cloned\");\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AccountGroup.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Account Group class. Helps to categorize account types to make reporting easier and consistent.\n * \n * @author Craig Cavanaugh\n */\npublic enum AccountGroup {\n\n    ASSET(ResourceUtils.getString(\"AccountType.Asset\")),\n    EQUITY(ResourceUtils.getString(\"AccountType.Equity\")),\n    EXPENSE(ResourceUtils.getString(\"AccountType.Expense\")),\n    INCOME(ResourceUtils.getString(\"AccountType.Income\")),\n    INVEST(ResourceUtils.getString(\"AccountType.Investment\")),\n    LIABILITY(ResourceUtils.getString(\"AccountType.Liability\")),\n    ROOT(ResourceUtils.getString(\"AccountType.Root\")),\n    SIMPLEINVEST(ResourceUtils.getString(\"AccountType.SimpleInvestment\")); // CD's, Treasuries, Etc.\n\n    private final transient String description;\n\n    AccountGroup(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AccountProxy.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.concurrent.locks.Lock;\n\nimport jgnash.time.DateUtils;\n\n/**\n * Proxy class to locate account balance behaviors. Depending on account type, summation of transaction types are\n * handled differently.\n *\n * @author Craig Cavanaugh\n */\nclass AccountProxy {\n\n    final Account account;\n\n    AccountProxy(final Account account) {\n        this.account = account;\n    }\n\n    /**\n     * Get the balance of all account transactions.\n     *\n     * @return the balance of this account\n     */\n    public BigDecimal getBalance() {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            BigDecimal balance = BigDecimal.ZERO;\n\n            for (Transaction transaction : account.getSortedTransactionList()) {\n                balance = balance.add(transaction.getAmount(account));\n            }\n\n            return balance;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Get the account balance up to a specified index.\n     *\n     * @param index the balance of this account at the specified index.\n     * @return the balance of this account at the specified index.\n     */\n    public BigDecimal getBalanceAt(final int index) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            BigDecimal balance = BigDecimal.ZERO;\n\n            List<Transaction> transactions = account.getSortedTransactionList();\n\n            for (int i = 0; i <= index; i++) {\n                balance = balance.add(transactions.get(i).getAmount(account));\n            }\n            return balance;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns the balance of the transactions inclusive of the start and end dates.\n     *\n     * @param start The inclusive start date\n     * @param end   The inclusive end date\n     * @return The ending balance\n     */\n    public BigDecimal getBalance(final LocalDate start, final LocalDate end) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            BigDecimal balance = BigDecimal.ZERO;\n\n            for (final Transaction t : account.getSortedTransactionList()) {\n                final LocalDate d = t.getLocalDate();\n\n                if (DateUtils.after(d, start) && DateUtils.before(d, end)) {\n                    balance = balance.add(t.getAmount(account));\n                }\n            }\n\n            return balance;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns the account balance up to and inclusive of the supplied date.\n     *\n     * @param date The inclusive ending date\n     * @return The ending balance\n     */\n    public BigDecimal getBalance(final LocalDate date) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            BigDecimal balance = BigDecimal.ZERO;\n\n            if (!account.transactions.isEmpty()) {\n                balance = getBalance(account.getSortedTransactionList().get(0).getLocalDate(), date);\n            }\n\n            return balance;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns the cash balance of this account.\n     *\n     * @return exception thrown\n     */\n    public BigDecimal getCashBalance() {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * Returns the market value of this account.\n     *\n     * @return exception thrown\n     */\n    public BigDecimal getMarketValue() {\n        throw new UnsupportedOperationException();\n    }\n\n    /**\n     * Calculates the reconciled balance of the account.\n     *\n     * @return the reconciled balance of this account\n     */\n    public BigDecimal getReconciledBalance() {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            BigDecimal balance = BigDecimal.ZERO;\n\n            // Use the cached list to avoid ConcurrentModificationException with JPA\n            for (final Transaction t : account.getSortedTransactionList()) {\n                if (t.getReconciled(account) == ReconciledState.RECONCILED) {\n                    balance = balance.add(t.getAmount(account));\n                }\n            }\n\n            return balance;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Get the default opening balance for reconciling the account.\n     *\n     * @return Opening balance for reconciling the account\n     */\n    public BigDecimal getOpeningBalanceForReconcile() {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            final LocalDate date = account.getFirstUnreconciledTransactionDate();\n\n            final List<Transaction> transactions = account.getSortedTransactionList();\n\n            BigDecimal balance = BigDecimal.ZERO;\n\n            for (int i = 0; i < transactions.size(); i++) {\n                if (transactions.get(i).getLocalDate().equals(date)) {\n                    if (i > 0) {\n                        balance = getBalanceAt(i - 1);\n                    }\n                    break;\n                }\n            }\n            return balance;\n        } finally {\n            l.unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AccountTreeXMLFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;\nimport com.thoughtworks.xstream.hibernate.converter.HibernatePersistentCollectionConverter;\nimport com.thoughtworks.xstream.hibernate.converter.HibernatePersistentMapConverter;\nimport com.thoughtworks.xstream.hibernate.converter.HibernateProxyConverter;\nimport com.thoughtworks.xstream.hibernate.mapper.HibernateMapper;\nimport com.thoughtworks.xstream.io.xml.PrettyPrintWriter;\nimport com.thoughtworks.xstream.io.xml.StaxDriver;\nimport com.thoughtworks.xstream.mapper.MapperWrapper;\nimport com.thoughtworks.xstream.security.ArrayTypePermission;\nimport com.thoughtworks.xstream.security.NoTypePermission;\nimport com.thoughtworks.xstream.security.PrimitiveTypePermission;\nimport com.thoughtworks.xstream.security.WildcardTypePermission;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.xstream.XStreamJVM9;\nimport jgnash.resource.util.ClassPathUtils;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Import and export a tree of accounts using XML files.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountTreeXMLFactory {\n    private static final Charset ENCODING = StandardCharsets.UTF_8;\n\n    private static final String RESOURCE_ROOT_PATH = \"/jgnash/resource/account\";\n\n    private AccountTreeXMLFactory() {\n    }\n    \n    private static XStream getStream() {\n\n        final XStreamJVM9 xstream = new XStreamJVM9(new PureJavaReflectionProvider(), new StaxDriver()) {\n\n            @Override\n            protected MapperWrapper wrapMapper(final MapperWrapper next) {\n                return new HibernateMapper(next);\n            }\n        };\n\n        // configure XStream security\n        xstream.addPermission(NoTypePermission.NONE);\n        xstream.addPermission(PrimitiveTypePermission.PRIMITIVES);\n        xstream.addPermission(ArrayTypePermission.ARRAYS);\n        xstream.addPermission(new WildcardTypePermission(new String[] {\"java.**\", \"jgnash.engine.**\"}));\n\n        xstream.ignoreUnknownElements();    // gracefully ignore fields in the file that do not have object members\n\n        xstream.setMode(XStream.ID_REFERENCES);\n\n        xstream.alias(\"date\", LocalDate.class); // use date instead of local-date by default\n\n        xstream.alias(\"Account\", Account.class);\n        xstream.alias(\"RootAccount\", RootAccount.class);\n        xstream.alias(\"CurrencyNode\", CurrencyNode.class);\n        xstream.alias(\"SecurityNode\", SecurityNode.class);\n\n        xstream.useAttributeFor(Account.class, \"placeHolder\");\n        xstream.useAttributeFor(Account.class, \"locked\");\n        xstream.useAttributeFor(Account.class, \"visible\");\n        xstream.useAttributeFor(Account.class, \"name\");\n        xstream.useAttributeFor(Account.class, \"description\");\n\n        xstream.useAttributeFor(CommodityNode.class, \"symbol\");\n        xstream.useAttributeFor(CommodityNode.class, \"scale\");\n        xstream.useAttributeFor(CommodityNode.class, \"prefix\");\n        xstream.useAttributeFor(CommodityNode.class, \"suffix\");\n        xstream.useAttributeFor(CommodityNode.class, \"description\");\n\n        xstream.omitField(StoredObject.class, \"uuid\");\n        xstream.omitField(StoredObject.class, \"markedForRemoval\");\n\n        // Ignore fields required for JPA\n        xstream.omitField(StoredObject.class, \"version\");\n\n        xstream.omitField(Account.class, \"transactions\");\n        xstream.omitField(Account.class, \"accountBalance\");\n        xstream.omitField(Account.class, \"reconciledBalance\");\n        xstream.omitField(Account.class, \"attributes\");\n        xstream.omitField(Account.class, \"propertyMap\");\n        xstream.omitField(Account.class, \"amortizeObject\");\n\n        xstream.omitField(SecurityNode.class, \"historyNodes\");\n        xstream.omitField(SecurityNode.class, \"securityHistoryEvents\");\n\n        // Filters out the hibernate\n        xstream.registerConverter(new HibernateProxyConverter());\n        xstream.registerConverter(new HibernatePersistentCollectionConverter(xstream.getMapper()));\n        xstream.registerConverter(new HibernatePersistentMapConverter(xstream.getMapper()));\n        \n        \n        //xstream.registerConverter(new HibernatePersistentSortedMapConverter(xstream.getMapper()));\n        //xstream.registerConverter(new HibernatePersistentSortedSetConverter(xstream.getMapper()));\n\n        return xstream;\n    }\n\n    public static void exportAccountTree(final Engine engine, final Path file) {\n        RootAccount account = engine.getRootAccount();\n\n        XStream xstream = getStream();\n\n        try (final Writer writer = Files.newBufferedWriter(file, ENCODING);\n             final ObjectOutputStream out = xstream.createObjectOutputStream(new PrettyPrintWriter(writer))) {\n            out.writeObject(account);\n        } catch (IOException e) {\n            Logger.getLogger(AccountTreeXMLFactory.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    /**\n     * Load an account tree given a reader.\n     *\n     * @param reader Reader to use\n     * @return RootAccount if reader is valid\n     */\n    private static RootAccount loadAccountTree(final Reader reader) {\n        RootAccount account = null;\n\n        XStream xstream = getStream();\n\n        try (final ObjectInputStream in = xstream.createObjectInputStream(reader)) {\n            final Object o = in.readObject();\n\n            if (o instanceof RootAccount) {\n                account = (RootAccount) o;\n            }\n        } catch (IOException | ClassNotFoundException ex) {\n            Logger.getLogger(AccountTreeXMLFactory.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        return account;\n    }\n\n    /**\n     * Load an account tree given a reader.\n     *\n     * @param file file name to use\n     * @return RootAccount if file name is valid\n     */\n    public static RootAccount loadAccountTree(final Path file) {\n        RootAccount account = null;\n\n        try (final Reader reader = Files.newBufferedReader(file, ENCODING)) {\n            account = loadAccountTree(reader);\n        } catch (IOException ex) {\n            Logger.getLogger(AccountTreeXMLFactory.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        return account;\n    }\n\n    /**\n     * Load an account tree given an InputStream.\n     *\n     * @param stream InputStream to use\n     * @return RootAccount if stream is valid\n     */\n    private static RootAccount loadAccountTree(final InputStream stream) {\n        try (Reader reader = new InputStreamReader(stream, ENCODING)) {\n            return loadAccountTree(reader);\n        } catch (IOException ex) {\n            Logger.getLogger(AccountTreeXMLFactory.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        return null;\n    }\n\n    /**\n     * Imports an account tree into the existing account tree. Account\n     * currencies are forced to the engine's default\n     *\n     * @param engine current engine to merge into\n     * @param root   root of account structure to merge\n     */\n    public static void importAccountTree(final Engine engine, final RootAccount root) {\n        AccountImport accountImport = new AccountImport();\n        accountImport.importAccountTree(engine, root);\n    }\n\n    /**\n     * Merges an account tree into the existing account tree. Duplicate\n     * currencies are prevented\n     *\n     * @param engine current engine to merge into\n     * @param root   root of account structure to merge\n     */\n    public static void mergeAccountTree(final Engine engine, final RootAccount root) {\n        AccountImport accountImport = new AccountImport();\n        accountImport.mergeAccountTree(engine, root);\n    }\n\n    public static Collection<RootAccount> getLocalizedAccountSet() {\n        final List<RootAccount> files = new ArrayList<>();\n\n        for (final String string : getAccountSetList()) {\n\n            try (final InputStream stream = AccountTreeXMLFactory.class.getResourceAsStream(string)) {\n                final RootAccount account = AccountTreeXMLFactory.loadAccountTree(stream);\n                files.add(account);\n            } catch (final IOException e) {\n                Logger.getLogger(AccountTreeXMLFactory.class.getName()).log(Level.SEVERE, null, e);\n            }\n        }\n\n        return files;\n    }\n\n    private static List<String> getAccountSetList() {\n        final String path = ClassPathUtils.getLocalizedPath(RESOURCE_ROOT_PATH);\n\n        final List<String> set = new ArrayList<>();\n\n        if (path != null) {\n            try (final InputStream stream = AccountTreeXMLFactory.class.getResourceAsStream(path + \"/set.txt\");\n                 final BufferedReader r = new BufferedReader(new InputStreamReader(stream, ENCODING))) {\n\n                String line = r.readLine();\n\n                while (line != null) {\n                    set.add(path + \"/\" + line);\n                    line = r.readLine();\n                }\n            } catch (final IOException ex) {\n                Logger.getLogger(AccountTreeXMLFactory.class.getName()).log(Level.SEVERE, null, ex);\n            }\n        }\n        return set;\n    }\n\n    private static class AccountImport {\n\n        // merge map for accounts\n        private final Map<Account, Account> mergeMap = new HashMap<>();\n\n        private void importAccountTree(final Engine engine, final RootAccount root) {\n            forceCurrency(engine, root);\n\n            for (Account child : root.getChildren()) {\n                importChildren(engine, child);\n            }\n        }\n\n        private void mergeAccountTree(final Engine engine, final RootAccount root) {\n            fixCurrencies(engine, root);\n\n            for (final Account child : root.getChildren()) {\n                importChildren(engine, child);\n            }\n        }\n\n        /**\n         * Ensures that duplicate currencies are not created when the accounts are merged.\n         *\n         * @param engine  Engine with existing currencies\n         * @param account account to correct\n         */\n        private static void fixCurrencies(final Engine engine, final Account account) {\n\n            // If an existing currency matches, assign it to the account\n            engine.getCurrencies().stream().filter(currencyNode -> account.getCurrencyNode()\n                    .matches(currencyNode)).forEach(account::setCurrencyNode);\n\n            // Need to persist the currency before the account if it does not exist within the database\n            if (!engine.getCurrencies().contains(account.getCurrencyNode())) {\n                engine.addCurrency(account.getCurrencyNode());\n            }\n\n            // match SecurityNodes to prevent duplicates\n            if (account.memberOf(AccountGroup.INVEST)) {\n                final Set<SecurityNode> nodes = account.getSecurities();\n\n                for (final SecurityNode node : nodes) {\n                    SecurityNode sNode = engine.getSecurity(node.getSymbol());\n\n                    if (sNode == null) { // no match found\n                        try {\n                            sNode = (SecurityNode) node.clone();\n\n                            for (final CurrencyNode currencyNode : engine.getCurrencies()) {\n                                if (sNode.getReportedCurrencyNode().matches(currencyNode)) {\n                                    sNode.setReportedCurrencyNode(currencyNode);\n                                }\n                            }\n                            if (!engine.addSecurity(sNode)) {\n                                final ResourceBundle rb = ResourceUtils.getBundle();\n                                Logger.getLogger(AccountImport.class.getName()).log(Level.SEVERE,\n                                        rb.getString(\"Message.Error.SecurityAdd\"), sNode.getSymbol());\n                            }\n                        } catch (final CloneNotSupportedException e) {\n                            Logger.getLogger(AccountImport.class.getName()).log(Level.SEVERE,\n                                    e.getLocalizedMessage(), e);\n                        }\n                    }\n\n                    account.removeSecurity(node);\n                    account.addSecurity(sNode);\n                }\n            }\n\n            for (Account child : account.getChildren()) {\n                fixCurrencies(engine, child);\n            }\n        }\n\n        /**\n         * Ensures that duplicate currencies are not created when the accounts are merged.\n         *\n         * @param engine  Engine with existing currencies\n         * @param account account to correct\n         */\n        private static void forceCurrency(final Engine engine, final Account account) {\n\n            account.setCurrencyNode(engine.getDefaultCurrency());\n\n            // match SecurityNodes to prevent duplicates\n            if (account.memberOf(AccountGroup.INVEST)) {\n                final Set<SecurityNode> nodes = account.getSecurities();\n\n                for (final SecurityNode node : nodes) {\n                    SecurityNode sNode = engine.getSecurity(node.getSymbol());\n\n                    if (sNode == null) { // no match found\n                        try {\n                            sNode = (SecurityNode) node.clone();\n\n                            sNode.setReportedCurrencyNode(engine.getDefaultCurrency());\n\n                            if (!engine.addSecurity(sNode)) {\n                                final ResourceBundle rb = ResourceUtils.getBundle();\n                                Logger.getLogger(AccountImport.class.getName()).log(Level.SEVERE,\n                                        rb.getString(\"Message.Error.SecurityAdd\"), sNode.getSymbol());\n                            }\n                        } catch (final CloneNotSupportedException e) {\n                            Logger.getLogger(AccountImport.class.getName()).log(Level.SEVERE, e.toString(), e);\n                        }\n                    }\n\n                    account.removeSecurity(node);\n                    account.addSecurity(sNode);\n                }\n            }\n\n            for (Account child : account.getChildren()) {\n                forceCurrency(engine, child);\n            }\n        }\n\n        private void importChildren(final Engine engine, final Account account) {\n\n            // fix the exchange rate DAO if needed\n            engine.attachCurrencyNode(account.getCurrencyNode());\n\n            // match RootAccount special case\n            if (account.getParent() instanceof RootAccount) {\n                mergeMap.put(account.getParent(), engine.getRootAccount());\n            }\n\n            // search for a pre-existing match\n            Account match = AccountUtils.searchTree(engine.getRootAccount(), account.getName(),\n                    account.getAccountType(), account.getDepth());\n\n            if (match != null && match.getParent().equals(mergeMap.get(account.getParent()))) { // found a match\n                mergeMap.put(account, match);\n            } else { // the account is unique\n\n                // place in the merge map\n                mergeMap.put(account, account);\n\n                Account parent = mergeMap.get(account.getParent());\n                engine.addAccount(parent, account);\n            }\n\n            for (final Account child : account.getChildren()) {\n                importChildren(engine, child);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AccountType.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.EnumSet;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Account type enumeration.\n * \n * @author Craig Cavanaugh\n */\npublic enum AccountType {\n\n    ASSET(ResourceUtils.getString(\"AccountType.Asset\"), AccountGroup.ASSET, AccountProxy.class, true),\n    BANK(ResourceUtils.getString(\"AccountType.Bank\"), AccountGroup.ASSET, AccountProxy.class, true),\n    CASH(ResourceUtils.getString(\"AccountType.Cash\"), AccountGroup.ASSET, AccountProxy.class, true),\n    CHECKING(ResourceUtils.getString(\"AccountType.Checking\"), AccountGroup.ASSET, AccountProxy.class, true),\n    CREDIT(ResourceUtils.getString(\"AccountType.Credit\"), AccountGroup.LIABILITY, AccountProxy.class, true),\n    EQUITY(ResourceUtils.getString(\"AccountType.Equity\"), AccountGroup.EQUITY, AccountProxy.class, true),\n    EXPENSE(ResourceUtils.getString(\"AccountType.Expense\"), AccountGroup.EXPENSE, AccountProxy.class, true),\n    INCOME(ResourceUtils.getString(\"AccountType.Income\"), AccountGroup.INCOME, AccountProxy.class, true),\n    INVEST(ResourceUtils.getString(\"AccountType.Investment\"), AccountGroup.INVEST, InvestmentAccountProxy.class, false),\n    SIMPLEINVEST(ResourceUtils.getString(\"AccountType.SimpleInvestment\"), AccountGroup.SIMPLEINVEST, AccountProxy.class, true),\n    LIABILITY(ResourceUtils.getString(\"AccountType.Liability\"), AccountGroup.LIABILITY, AccountProxy.class, true),\n    MONEYMKRT(ResourceUtils.getString(\"AccountType.MoneyMarket\"), AccountGroup.ASSET, AccountProxy.class, true),\n    MUTUAL(ResourceUtils.getString(\"AccountType.Mutual\"), AccountGroup.INVEST, InvestmentAccountProxy.class, false),\n    ROOT(ResourceUtils.getString(\"AccountType.Root\"), AccountGroup.ROOT, AccountProxy.class, true);\n\n    private final transient String description;\n\n    private final transient AccountGroup accountGroup;\n\n    private final transient Class<? extends AccountProxy> accountProxy;\n\n    private final transient boolean mutable;\n\n    AccountType(final String description, final AccountGroup accountGroup, final Class<? extends AccountProxy> accountProxy, final boolean mutable) {\n        this.description = description;\n        this.accountGroup = accountGroup;\n        this.accountProxy = accountProxy;\n        this.mutable = mutable;\n    }\n\n    /**\n     * Returns all AccountTypes that fit the supplied AccountGroup.\n     * \n     * @param group AccountGroup to match\n     * @return array of AccountTypes that fit the supplied group\n     */\n    public static Set<AccountType> getAccountTypes(final AccountGroup group) {\n        final Set<AccountType> list = getAccountTypeSet();\n\n        list.removeIf(accountType -> accountType.getAccountGroup() != group);\n\n        return list;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n    public AccountGroup getAccountGroup() {\n        return accountGroup;\n    }\n\n    public boolean isMutable() {\n        return mutable;\n    }\n\n    private static Set<AccountType> getAccountTypeSet() {\n        Set<AccountType> set = EnumSet.allOf(AccountType.class);\n\n        set.remove(AccountType.ROOT);\n        return set;\n    }\n\n    AccountProxy getProxy(final Account account) {\n        try {\n            Class<?>[] constParams = new Class<?>[] { Account.class };\n            Constructor<?> accConst = accountProxy.getDeclaredConstructor(constParams);\n            Object[] params = new Object[] { account };\n            return (AccountProxy) accConst.newInstance(params);\n        } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {\n            Logger.getLogger(AccountType.class.getName()).log(Level.SEVERE, null, ex);\n        }\n        return null; // unable to create object\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AccountUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\n/**\n * Static account utilities.\n * \n * @author Craig Cavanaugh\n */\nclass AccountUtils {\n\n    /**\n     * Searches an account tree given the supplied parameters.\n     * \n     * @param root Base account\n     * @param name Account name\n     * @param type Account type\n     * @param depth Account depth\n     * @return matched account if it exists\n     */\n    static Account searchTree(final Account root, final String name, final AccountType type, final int depth) {\n        Account match = null;\n\n        for (final Account a : root.getChildren()) {\n            if (a.getName().equals(name) && a.getAccountType() == type && a.getDepth() == depth) {\n                match = a;\n            } else if (a.getChildCount() > 0) {\n                match = searchTree(a, name, type, depth);\n            }\n\n            if (match != null) {\n                break;\n            }\n        }\n        return match;\n    }\n\n    private AccountUtils() {\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AmortizeObject.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Objects;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.SequenceGenerator;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * This class is used to calculate loan payments.\n * <p>\n * Because BigDecimal is lacking methods of exponents, calculations are\n * performed using StrictMath to maintain portability. Results are returned as\n * doubles. Results will need to be scaled and rounded.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class AmortizeObject implements Serializable {\n\n    @SuppressWarnings(\"unused\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    private long id;\n\n    @ManyToOne\n    private Account interestAccount; // account for interest payment\n\n    @ManyToOne\n    private Account bankAccount; // account for principal account\n\n    // (normally the liability account)\n    @ManyToOne\n    private Account feesAccount; // account to place non interest fees\n\n    /**\n     * Controls the type of transaction automatically generated\n     */\n    @SuppressWarnings(\"unused\")\n    private Integer transactionType;\n\n    /**\n     * the number of payments per year.\n     */\n    private int numPayments;\n\n    /**\n     * length of loan in months.\n     */\n    private int length;\n\n    /**\n     * the number of compounding periods per year.\n     */\n    private int numCompPeriods;\n\n    /**\n     * annual interest rate, APR (ex 6.75).\n     */\n    private BigDecimal interestRate;\n\n    /**\n     * original balance of the loan.\n     */\n    private BigDecimal originalBalance;\n\n    /**\n     * PMI, escrow, etc.\n     */\n    private BigDecimal fees = BigDecimal.ZERO;\n\n    /**\n     * the payee to use.\n     */\n    private String payee;\n\n    /**\n     * the memo to use.\n     */\n    private String memo;\n\n    // private String checkNumber;     // check number to use\n\n    /**\n     * origination date.\n     */\n    private LocalDate date = LocalDate.now();\n\n    /**\n     * calculate interest based on daily periodic rate.\n     */\n    private boolean useDailyRate;\n\n    /**\n     * the number of days per year for daily periodic rate.\n     */\n    private BigDecimal daysPerYear;\n\n    /**\n     * Empty constructor to keep reflection happy.\n     */\n    public AmortizeObject() {\n    }\n\n    public void setDate(final LocalDate localDate) {\n        date = localDate;\n    }\n\n    public LocalDate getDate() {\n        return date;\n    }\n\n    public void setPaymentPeriods(final int periods) {\n        numPayments = periods;\n    }\n\n    public int getPaymentPeriods() {\n        return numPayments;\n    }\n\n    /**\n     * Sets the length of the loan (in months).\n     *\n     * @param months length of loan\n     */\n    public void setLength(final int months) {\n        this.length = months;\n    }\n\n    /**\n     * Gets the length of the loan in months.\n     *\n     * @return length of loan in months\n     */\n    public int getLength() {\n        return length;\n    }\n\n    /**\n     * Determines if interest will be calculate based on a daily periodic rate,\n     * or if it is assumed that the interest is paid exactly on the due date.\n     *\n     * @param daily true if interest should be calculated using a daily rate\n     */\n    public void setUseDailyRate(final boolean daily) {\n        useDailyRate = daily;\n    }\n\n    /**\n     * Returns how interest will be calculated.\n     *\n     * @return true if interest is calculated using the daily periodic rate\n     */\n    public boolean getUseDailyRate() {\n        return useDailyRate;\n    }\n\n    /**\n     * Sets the number of days per year used to calculate the daily periodic\n     * interest rate. The value can be a decimal.\n     *\n     * @param days The number of days in a year\n     */\n    public void setDaysPerYear(final BigDecimal days) {\n        daysPerYear = days;\n    }\n\n    /**\n     * Returns the number of days per year used to calculate a daily periodic\n     * interest rate.\n     *\n     * @return The number of days per year\n     */\n    public BigDecimal getDaysPerYear() {\n        return daysPerYear;\n    }\n\n    public void setRate(final BigDecimal rate) {\n        interestRate = rate;\n    }\n\n    public BigDecimal getRate() {\n        return interestRate;\n    }\n\n    public void setPrincipal(final BigDecimal principal) {\n        originalBalance = principal;\n    }\n\n    public BigDecimal getPrincipal() {\n        return originalBalance;\n    }\n\n    public void setInterestPeriods(final int periods) {\n        numCompPeriods = periods;\n    }\n\n    public int getInterestPeriods() {\n        return numCompPeriods;\n    }\n\n    public void setFees(final BigDecimal fees) {\n        this.fees = Objects.requireNonNullElse(fees, BigDecimal.ZERO);\n    }\n\n    public BigDecimal getFees() {\n        return fees;\n    }\n\n    /**\n     * Set the id of the interest account.\n     *\n     * @param id the id of the interest account\n     */\n    public void setInterestAccount(final Account id) {\n        interestAccount = id;\n    }\n\n    /**\n     * Returns the id of the interest account.\n     *\n     * @return the id of the interest account\n     */\n    public Account getInterestAccount() {\n        return interestAccount;\n    }\n\n    /**\n     * Set the id of the principal account.\n     *\n     * @param id the id of the principal account\n     */\n    public void setBankAccount(final Account id) {\n        bankAccount = id;\n    }\n\n    /**\n     * Returns the id of the principal account.\n     *\n     * @return the id of the principal account\n     */\n    public Account getBankAccount() {\n        return bankAccount;\n    }\n\n    /**\n     * Set the id of the fees account.\n     *\n     * @param id the id of the fees account\n     */\n    public void setFeesAccount(final Account id) {\n        feesAccount = id;\n    }\n\n    /**\n     * Returns the id of the fees account.\n     *\n     * @return the id of the fees account\n     */\n    public Account getFeesAccount() {\n        return feesAccount;\n    }\n\n    public void setPayee(final String payee) {\n        this.payee = payee;\n    }\n\n    public String getPayee() {\n        return payee;\n    }\n\n    public void setMemo(String memo) {\n        this.memo = memo;\n    }\n\n    public String getMemo() {\n        return memo;\n    }\n\n    /**\n     * Calculates the effective interest rate.<br>\n     * Ie = (1 + i/m)^(m/n) - 1 <br>\n     * n = payments per period m = number of times compounded per period\n     *\n     * @return effective interest rate\n     */\n    private double getEffectiveInterestRate() {\n        if (interestRate != null && numPayments > 0 && numCompPeriods > 0) {\n            double i = interestRate.doubleValue() / 100.0;\n            return StrictMath.pow(1.0 + i / numCompPeriods, (double) numCompPeriods / (double) numPayments) - 1.0;\n        }\n        return 0.0;\n    }\n\n    /**\n     * Calculates the daily interest rate.<br>\n     * This works for US, can't find any information on Canada\n     *\n     * @return periodic interest rate\n     */\n    private double getDailyPeriodicInterestRate() {\n        if (interestRate != null && numPayments > 0 && numCompPeriods > 0 && daysPerYear != null) {\n            double rate = getEffectiveInterestRate();\n            rate = rate * numPayments;\n            return rate / daysPerYear.doubleValue();\n        }\n        return 0.0;\n    }\n\n    //    /**\n    //     * Calculates the sum of compounded interest and principal\n    //     * S = P(1+Ie)^n*term\n    //     *\n    //     * @return sum with interest\n    //     */\n    //    double getSumWithInterest() {\n    //        return getPIPayment() * length;\n    //    }\n\n    //    public double getTotalInterestPaid() {\n    //        return getSumWithInterest() - originalBalance.doubleValue();\n    //    }\n\n    /**\n     * Calculates the principal and interest payment of an equal payment series\n     * M = P * ( Ie / (1 - (1 + Ie) ^ -N)) N = total number of periods the loan\n     * is amortized over.\n     *\n     * @return P and I\n     */\n    private double getPIPayment() {\n\n        // zero interest loan\n        if ((interestRate == null || interestRate.compareTo(BigDecimal.ZERO) == 0) && length > 0 && numPayments > 0\n                && originalBalance != null) {\n\n            return originalBalance.doubleValue() / ((length / 12.0) * numPayments);\n        }\n\n        if (length > 0 && numPayments > 0 && numCompPeriods > 0 && originalBalance != null) {\n            double i = getEffectiveInterestRate();\n            double p = originalBalance.doubleValue();\n\n            return p * (i / (1.0 - StrictMath.pow(1.0 + i, length * -1.0)));\n        }\n        return 0.0;\n    }\n\n    /**\n     * Calculates the principal and interest plus finance charges.\n     *\n     * @return the payment\n     */\n    private double getPayment() {\n        return getPIPayment() + fees.doubleValue();\n    }\n\n    /**\n     * Calculates the interest portion of the next loan payment given the\n     * remaining loan balance.\n     *\n     * @param balance remaining balance\n     * @return interest\n     */\n    private double getIPayment(final BigDecimal balance) {\n        if (balance != null) {\n            double i = getEffectiveInterestRate();\n            return i * balance.doubleValue();\n        }\n        return 0.0;\n    }\n\n    /**\n     * Calculates the interest portion of the next loan payment given the\n     * remaining loan balance and the dates between payments.\n     *\n     * @param balance balance\n     * @param start   start date\n     * @param end     end date\n     * @return interest\n     */\n    private double getIPayment(final BigDecimal balance, final LocalDate start, final LocalDate end) {\n        if (balance != null) {\n\n            int dayEnd = end.getDayOfYear();\n            int dayStart = start.getDayOfYear();\n\n            int days = Math.abs(dayEnd - dayStart);\n\n            double i = getDailyPeriodicInterestRate();\n            return i * days * balance.doubleValue();\n        }\n        return 0.0;\n    }\n\n    //\t/**\n    //     * Calculates the principal portion of the next loan payment given the\n    //     * remaining loan balance\n    //     *\n    //     * @param balance balance\n    //     * @return principal\n    //     */\n    //\tpublic double getPPayment(BigDecimal balance) {\n    //\t\treturn getPIPayment() - getIPayment(balance);\n    //\t}\n\n    @Nullable\n    public Transaction generateTransaction(@NotNull final Account account, @NotNull final LocalDate date, final String number) {\n\n        BigDecimal balance = account.getBalance().abs();\n        double payment = getPayment();\n\n        double interest;\n\n        if (getUseDailyRate()) {\n\n            LocalDate last;\n\n            if (account.getTransactionCount() > 0) {\n                last = account.getTransactionAt(account.getTransactionCount() - 1).getLocalDate();\n            } else {\n                last = date;\n            }\n\n            interest = getIPayment(balance, last, date); // get the interest portion\n\n        } else {\n            interest = getIPayment(balance); // get the interest portion\n        }\n\n        // get debit account\n        final Account bank = getBankAccount();\n\n        if (bank != null) {\n            CommodityNode n = bank.getCurrencyNode();\n\n            Transaction transaction = new Transaction();\n            transaction.setDate(date);\n            transaction.setNumber(number);\n            transaction.setPayee(getPayee());\n\n            // transaction is made relative to the debit/checking account\n\n            TransactionEntry entry = new TransactionEntry();\n\n            // this entry is the principal payment\n            entry.setCreditAccount(account);\n            entry.setDebitAccount(bank);\n            entry.setAmount(n.round(payment - interest));\n            entry.setMemo(getMemo());\n\n            transaction.addTransactionEntry(entry);\n\n            // handle interest portion of the payment\n            Account i = getInterestAccount();\n            if (i != null && interest != 0.0) {\n                entry = new TransactionEntry();\n                entry.setCreditAccount(i);\n                entry.setDebitAccount(bank);\n                entry.setAmount(n.round(interest));\n                entry.setMemo(ResourceUtils.getString(\"Word.Interest\"));\n                transaction.addTransactionEntry(entry);\n            }\n\n            // a fee has been assigned\n            if (getFees().compareTo(BigDecimal.ZERO) != 0) {\n                Account f = getFeesAccount();\n                if (f != null) {\n                    entry = new TransactionEntry();\n                    entry.setCreditAccount(f);\n                    entry.setDebitAccount(bank);\n                    entry.setAmount(getFees());\n                    entry.setMemo(ResourceUtils.getString(\"Word.Fees\"));\n                    transaction.addTransactionEntry(entry);\n                }\n            }\n\n            return transaction;\n        }\n\n        return null;\n    }\n\n\n     //Creates a payment transaction relative to the liability account\n    /*\n    private void paymentActionLiability() {\n\n    AmortizeObject ao = ((LiabilityAccount)account).getAmortizeObject();\n    Transaction tran = null;\n\n    if (ao != null) {\n    DateChkNumberDialog d = new DateChkNumberDialog(null, engine.getAccount(ao.getInterestAccount()));\n    d.show();\n\n    if (!d.getResult()) {\n    return;\n    }\n\n    BigDecimal balance = account.getBalance().abs();\n    BigDecimal fees = ao.getFees();\n    double payment = ao.getPayment();\n\n    double interest;\n\n    if (ao.getUseDailyRate()) {\n    Date today = d.getDate();\n    Date last = account.getTransactionAt(account.getTransactionCount() - 1).getDate();\n    interest = ao.getIPayment(balance, last, today); // get the interest portion\n    } else {\n    interest = ao.getIPayment(balance); // get the interest portion\n    }\n\n    Account b = engine.getAccount(ao.getBankAccount());\n    if (b != null) {\n    CommodityNode n = b.getCommodityNode();\n    SplitEntryTransaction e;\n\n    SplitTransaction t = new SplitTransaction(b.getCommodityNode());\n    t.setAccount(b);\n    t.setMemo(ao.getMemo());\n    t.setPayee(ao.getPayee());\n    t.setNumber(d.getNumber());\n    t.setDate(d.getDate());\n\n    // this entry is the complete payment\n    e = new SplitEntryTransaction(n);\n    e.setCreditAccount(account);\n    e.setDebitAccount(b);\n    e.setAmount(n.round(payment));\n    e.setMemo(ao.getMemo());\n    t.addSplit(e);\n\n    try {   // maintain transaction order (stretch time)\n    Thread.sleep(2);\n    } catch (Exception ie) {}\n\n    // handle interest portion of the payment\n    Account i = engine.getAccount(ao.getInterestAccount());\n    if (i != null) {\n    e = new SplitEntryTransaction(n);\n    e.setCreditAccount(i);\n    e.setDebitAccount(account);\n    e.setAmount(n.round(interest));\n    e.setMemo(rb.getString(\"Word.Interest\"));\n    t.addSplit(e);\n    }\n\n    try {   // maintain transaction order (stretch time)\n    Thread.sleep(2);\n    } catch (Exception ie) {}\n\n    // a fee has been assigned\n    if (ao.getFees().compareTo(new BigDecimal(\"0\")) != 0) {\n    Account f = engine.getAccount(ao.getFeesAccount());\n    if (f != null) {\n    e = new SplitEntryTransaction(n);\n    e.setCreditAccount(f);\n    e.setDebitAccount(account);\n    e.setAmount(ao.getFees());\n    e.setMemo(rb.getString(\"Word.Fees\"));\n    t.addSplit(e);\n    }\n    }\n\n    // the total should be the debit to the checking account\n    tran = t;\n    }\n    }\n\n    if (tran != null) {// display the transaction in the register\n    newTransaction(tran);\n    } else {    // could not generate the transaction\n    if (ao == null) {\n    Logger.getLogger(\"jgnashEngine\").warning(\"Please configure amortization\");\n    } else {\n    Logger.getLogger(\"jgnashEngine\").warning(\"Not enough information\");\n    }\n    }\n    }*/\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/AttachmentUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Support methods for handling attachments.\n *\n * @author Craig Cavanaugh\n */\npublic class AttachmentUtils {\n\n    private static final String ATTACHMENT_BASE = \"attachments\";\n\n    /**\n     * Utility class.\n     */\n    private AttachmentUtils() {\n    }\n\n    /**\n     * Creates the attachment directory for the active database.\n     * \n     * @param baseFile base directory for file attachments\n     * @return {@code true} if and only if the directory was created or if\n     *         it already exists; {@code false} otherwise\n     */\n    public static boolean createAttachmentDirectory(final Path baseFile) {\n        boolean result = false;\n\n        final Path attachmentPath = getAttachmentDirectory(baseFile);\n\n        if (attachmentPath != null && Files.notExists(attachmentPath)) {\n            try {\n                Files.createDirectories(attachmentPath);\n                result = true;\n            } catch (IOException e) {\n                Logger.getLogger(AttachmentUtils.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        } else {\n            result = true;\n        }\n\n        return result;\n    }\n\n    /**\n     * Returns the default attachment directory for the given base file.\n     *\n     * @param baseFile base file for attachment directory\n     * @return directory for all attachments\n     */\n    public static Path getAttachmentDirectory(@NotNull final Path baseFile) {\n        Objects.requireNonNull(baseFile);\n\n        if (baseFile.getParent() != null) {\n            return Paths.get(baseFile.getParent() + FileUtils.SEPARATOR + ATTACHMENT_BASE);\n        }\n\n        return null;\n    }\n\n    public static Path getAttachmentPath() {\n        return getAttachmentDirectory(Paths.get(EngineFactory.getActiveDatabase()));\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/CashFlow.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static java.lang.Math.abs;\nimport static java.time.temporal.ChronoUnit.DAYS;\n\n/**\n * Stores a history of cash flow items and calculates their internal rate of\n * return. It assumes 365 days per year (Actual/365 Fixed day count convention)\n * and uses a simple iterative solver.\n *\n * @author t-pa\n * @author Craig Cavanaugh\n */\npublic class CashFlow {\n\n    private static final double DAYS_PER_YEAR = 365;\n    private static final int MAX_ITERATIONS = 1000;\n    private static final double CONVERGENCE = 1.e-5;\n\n    private static final Logger logger = Logger.getLogger(CashFlow.class.getName());\n\n    private static class CashFlowItem {\n        final LocalDate date;\n        final BigDecimal amount;\n\n        CashFlowItem(final LocalDate date, final BigDecimal amount) {\n            this.date = date;\n            this.amount = amount;\n        }\n\n        @Override\n        public String toString() {\n            return String.format(\"[%s, %f]\", date.toString(), amount);\n        }\n    }\n\n    private final List<CashFlowItem> cashFlows = new ArrayList<>();\n\n    /**\n     * Add an item to the history of cash flows.\n     *\n     * @param date   the date of the cash flow\n     * @param amount the amount; negative for an investment, positive for a payout\n     */\n    public void add(final LocalDate date, final BigDecimal amount) {\n        cashFlows.add(new CashFlowItem(date, amount));\n    }\n\n    /**\n     * Calculate the internal rate of return of the cash flow. If the iterative\n     * solution does not converge, NaN is returned.\n     *\n     * @return an approximation of the (annualized) internal rate of return\n     */\n    public double internalRateOfReturn() {\n        if (cashFlows.isEmpty()) {\n            return 0.0;\n        }\n\n        // the reference date is arbitrary, but for better numerical accuracy,\n        // use one of the actual dates in the cash flow history\n        LocalDate referenceDate = cashFlows.get(0).date;\n\n        double lastRate = 0.0;\n        double lastNPV = netPresentValue(referenceDate, lastRate);\n\n        double rate = (lastNPV > 0) ? 0.05 : -0.05;\n\n        // iteratively calculate the IRR with the secant method\n        int i = 0;\n        boolean hasConverged = false;\n\n        do {\n            double npv = netPresentValue(referenceDate, rate);\n            double newRate = rate - npv * (rate - lastRate) / (npv - lastNPV);\n\n            lastRate = rate;\n            lastNPV = npv;\n            rate = newRate;\n\n            i++;\n\n            if (Double.isNaN(rate)) {   // check for failure to converge\n                break;\n            } else if (rate != 0 || lastRate != 0) {\n                hasConverged = abs(rate - lastRate) / (abs(rate) + abs(lastRate)) < CONVERGENCE;\n            } else {\n                hasConverged = true;\n            }\n        } while (!hasConverged && i < MAX_ITERATIONS);\n\n        if (!hasConverged) {\n            rate = Double.NaN;\n            logger.log(Level.INFO, \"IRR calculation did not converge. Data: {0}\", cashFlows);\n        }\n\n        return rate;\n    }\n\n    /**\n     * Calculate the net present value of the cash flow.\n     *\n     * @param referenceDate the NPV is relative to this date\n     * @param rate          the discount rate\n     * @return the net present value\n     */\n    private double netPresentValue(final LocalDate referenceDate, final double rate) {\n        double npv = 0;\n\n        for (final CashFlowItem item : cashFlows) {\n            double timeDifference = referenceDate.until(item.date, DAYS) / DAYS_PER_YEAR;\n            npv += item.amount.doubleValue() / StrictMath.pow(1 + rate, timeDifference);\n        }\n\n        return npv;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/CommodityNode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport javax.persistence.Entity;\nimport java.math.BigDecimal;\n\nimport jgnash.util.NotNull;\n\n/**\n * Abstract class for representing a commodity.\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic abstract class CommodityNode extends StoredObject implements Comparable<CommodityNode> {\n\n    private String symbol;\n\n    private byte scale = 2;\n\n    private String prefix = \"\";\n\n    private String suffix = \"\";\n\n    private String description;\n\n    public void setScale(final byte scale) {\n        this.scale = scale;\n    }\n\n    public byte getScale() {\n        return scale;\n    }\n\n    public void setSymbol(final String symbol) {\n        this.symbol = symbol;\n    }\n\n    public String getSymbol() {\n        return symbol;\n    }\n\n    public void setDescription(final String description) {\n        this.description = description;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setPrefix(final String prefix) {\n        this.prefix = prefix;\n    }\n\n    public String getPrefix() {\n        return prefix;\n    }\n\n    public void setSuffix(final String suffix) {\n        this.suffix = suffix;\n    }\n\n    public String getSuffix() {\n        return suffix;\n    }\n\n    @Override\n    public String toString() {\n        if (description != null) {\n            return symbol + \" (\" + description + ')';\n        }\n        return symbol;\n    }\n\n    @Override\n    public int compareTo(@NotNull final CommodityNode node) {\n        return symbol.compareTo(node.symbol);\n    }\n\n    @Override\n    public boolean equals(final Object other) {\n        return this == other || other instanceof CommodityNode && getUuid().equals(((CommodityNode) other).getUuid());\n    }\n\n    /**\n     * Determines if given node matches this node.\n     * <p>\n     * The UUID is not used for comparison if equals fails.\n     * \n     * @param other CurrencyNode to compare against\n     * @return true if objects match\n     */\n    public boolean matches(final CommodityNode other) {\n        boolean result = equals(other);\n\n        if (!result) {\n            result = getSymbol().equals(other.getSymbol());\n        }\n        return result;\n    }\n\n    /**\n     * Rounds a supplied double to the correct scale and returns a BigDecimal.\n     * \n     * @param value double to round\n     * @return properly scaled BigDecimal\n     */\n    public BigDecimal round(final double value) {\n        return new BigDecimal(value).setScale(scale, MathConstants.roundingMode);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/Comparators.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.Serializable;\nimport java.time.LocalDate;\nimport java.util.*;\n\n/**\n * Utility class consisting of {@code Comparators} useful for sorting lists of {@code StoredObject}\n * with the same and mixed inheritance.\n *\n * @author Craig Cavanaugh\n */\npublic class Comparators {\n\n    public static Comparator<Account> getAccountByCode() {\n        return new AccountByCode();\n    }\n\n    public static Comparator<Account> getAccountByName() {\n        return new AccountByName();\n    }\n\n    public static Comparator<Account> getAccountByPathName() {\n        return new AccountByPathName();\n    }\n\n    public static Comparator<Account> getAccountByBalance(LocalDate startDate, LocalDate endDate, CurrencyNode currency, boolean ascending) {\n        return new AccountByBalance(startDate, endDate, currency, ascending);\n    }\n\n    /**\n     * Sort {@code Account}s according to their position in the account tree. Parent accounts are\n     * sorted before their children.\n     *\n     * @param subComparator defines the sort order of accounts which have the same parent account\n     * @return the {@code Comparator}\n     */\n    public static Comparator<Account> getAccountByTreePosition(Comparator<Account> subComparator) {\n        return new AccountByTreePosition(subComparator);\n    }\n\n    private static class AccountByCode implements Comparator<Account>, Serializable {\n\n        @Override\n        public int compare(final Account a1, final Account a2) {\n\n            // Sort by account code first\n            int result = Integer.compare(a1.getAccountCode(), a2.getAccountCode());\n            if (result != 0) {\n                return result;\n            }\n\n            return a1.getName().compareTo(a2.getName());\n        }\n    }\n\n    private static class AccountByName implements Comparator<Account>, Serializable {\n\n        @Override\n        public int compare(final Account a1, final Account a2) {\n            return a1.getName().compareTo(a2.getName());\n        }\n    }\n\n    private static class AccountByPathName implements Comparator<Account>, Serializable {\n\n        @Override\n        public int compare(Account a1, Account a2) {\n            return a1.getPathName().compareTo(a2.getPathName());\n        }\n    }\n\n    private static class AccountByBalance implements Comparator<Account>, Serializable {\n\n        private final LocalDate startDate;\n\n        private final LocalDate endDate;\n\n        private final boolean ascending;\n\n        private final CurrencyNode currency;\n\n        AccountByBalance(final LocalDate startDate, final LocalDate endDate, CurrencyNode currency, boolean ascending) {\n            this.startDate = startDate;\n            this.endDate = endDate;\n            this.currency = currency;\n            this.ascending = ascending;\n        }\n\n        @Override\n        public int compare(Account a1, Account a2) {\n            int result = a1.getBalance(startDate, endDate, currency)\n                    .compareTo(a2.getBalance(startDate, endDate, currency));\n            if (!ascending) {\n                result *= -1;\n            }\n            return result;\n        }\n    }\n\n    private static class AccountByTreePosition implements Comparator<Account>, Serializable {\n\n        private final Comparator<Account> subComparator;\n\n        AccountByTreePosition(final Comparator<Account> subComparator) {\n            this.subComparator = subComparator;\n        }\n\n        private static Deque<Account> accountPath(Account acc) {\n            final Deque<Account> path = new LinkedList<>();\n\n            while (acc != null) {\n                path.addFirst(acc);\n                acc = acc.getParent();\n            }\n            return path;\n        }\n\n        @Override\n        public int compare(final Account a1, final Account a2) {\n            final Deque<Account> path1 = accountPath(a1);\n            final Deque<Account> path2 = accountPath(a2);\n\n            // find the first non-common ancestors\n            Account pa1, pa2;\n\n            do {\n                pa1 = path1.pollFirst();\n                pa2 = path2.pollFirst();\n            } while (pa1 != null && pa1.equals(pa2));\n\n            if (pa1 == null && pa2 == null) {\n                // this can only happen if a1 equals a2\n                return 0;\n            }\n\n            // if one of the paths ended, this is an ancestor of the other one, so sort it first;\n            // otherwise, let the subComparator decide on the same-level ancestors\n            return (pa1 == null) ? -1 : (pa2 == null) ? 1 : subComparator.compare(pa1, pa2);\n        }\n\n    }\n\n    /**\n     * Explicit order Comparator.\n     *\n     * @param <T> object type that is being sorted\n     */\n    public static class ExplicitComparator<T> implements Comparator<T>, Serializable {\n\n        final List<T> order = new ArrayList<>();\n\n        @SafeVarargs\n        public ExplicitComparator(final T... objects) {\n            Collections.addAll(order, objects);\n        }\n\n        @Override\n        public int compare(final T t1, final T t2) {\n            return Integer.compare(order.indexOf(t1), order.indexOf(t2));\n        }\n    }\n\n    private Comparators() {\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/Config.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.Entity;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.PostLoad;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * A general configuration class so that global configuration information may be stored inside the database.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class Config extends StoredObject {\n\n    private static final String CREATE_BACKUPS = \"CreateBackups\";\n\n    private static final String MAX_BACKUPS = \"MaxBackups\";\n\n    private static final String REMOVE_BACKUPS = \"RemoveBackups\";\n\n    private static final String LAST_SECURITIES_UPDATE_TIMESTAMP = \"LastSecuritiesUpdateTimestamp\";\n\n    private static final int MAX_BACKUPS_DEFAULT = 5;\n\n    @ManyToOne(cascade = CascadeType.PERSIST)\n    private CurrencyNode defaultCurrency;\n\n    private String accountSeparator = \":\";\n\n    /**\n     * Current file format\n     */\n    private String fileFormat = Engine.CURRENT_MAJOR_VERSION + \".\" + Engine.CURRENT_MINOR_VERSION;\n\n    private transient ReadWriteLock preferencesLock;\n\n    /**\n     * Contains a list a items to display in the transaction number combo.\n     */\n    @ElementCollection\n    private final List<String> transactionNumberItems = new ArrayList<>();\n\n    /**\n     * {@code Map} for file based operation preferences.\n     * <p>\n     * GUI locations, last used accounts, etc\n     * should not be stored here as they will be dependent on the user.\n     * Only values required for operation consistency should be stored here.\n     */\n    @ElementCollection\n    @Column(columnDefinition = \"varchar(8192)\")\n    private final Map<String, String> preferences = new HashMap<>();\n\n    public Config() {\n        preferencesLock = new ReentrantReadWriteLock(true);\n    }\n\n    void initialize() {\n        Account.setAccountSeparator(getAccountSeparator());\n    }\n\n    public String getFileFormat() {\n        return fileFormat;\n    }\n\n    int getMajorFileFormatVersion() {\n        return Integer.parseInt(getFileFormat().split(\"\\\\.\")[0]);\n    }\n\n    int getMinorFileFormatVersion() {\n        return Integer.parseInt(getFileFormat().split(\"\\\\.\")[1]);\n    }\n\n    void updateFileVersion() {\n        this.fileFormat = Engine.CURRENT_MAJOR_VERSION + \".\" + Engine.CURRENT_MINOR_VERSION;\n    }\n\n    void setDefaultCurrency(final CurrencyNode defaultCurrency) {\n        this.defaultCurrency = defaultCurrency;\n    }\n\n    CurrencyNode getDefaultCurrency() {\n        return defaultCurrency;\n    }\n\n    void setAccountSeparator(final String accountSeparator) {\n        this.accountSeparator = accountSeparator;\n        Account.setAccountSeparator(this.accountSeparator);\n    }\n\n    String getAccountSeparator() {\n        return accountSeparator;\n    }\n\n    void setTransactionNumberList(final List<String> transactionNumberItems) {\n        if (transactionNumberItems != null) {\n            this.transactionNumberItems.clear();\n            this.transactionNumberItems.addAll(transactionNumberItems);\n        }\n    }\n\n    List<String> getTransactionNumberList() {\n        if (transactionNumberItems.isEmpty()) {\n            final ResourceBundle rb = ResourceUtils.getBundle();\n\n            transactionNumberItems.add(rb.getString(\"Item.EFT\"));\n            transactionNumberItems.add(rb.getString(\"Item.Trans\"));\n        }\n        return new ArrayList<>(transactionNumberItems);\n    }\n\n    void setPreference(@NotNull final String key, @Nullable final String value) {\n\n        preferencesLock.writeLock().lock();\n\n        try {\n            if (key.isEmpty()) {\n                throw new RuntimeException(ResourceUtils.getString(\"Message.Error.EmptyKey\"));\n            }\n\n            if (value == null) {    // find and remove\n                preferences.remove(key);\n            } else {\n                preferences.put(key, value);\n            }\n        } finally {\n            preferencesLock.writeLock().unlock();\n        }\n    }\n\n    @Nullable\n    String getPreference(@NotNull final String key) {\n        preferencesLock.readLock().lock();\n\n        try {\n            if (key.isEmpty()) {\n                throw new RuntimeException(ResourceUtils.getString(\"Message.Error.EmptyKey\"));\n            }\n\n            return preferences.get(key);\n        } finally {\n            preferencesLock.readLock().unlock();\n        }\n    }\n\n    boolean createBackups() {\n        final String result = getPreference(CREATE_BACKUPS);\n\n        return result == null || Boolean.parseBoolean(result);\n    }\n\n    void setCreateBackups(final boolean createBackups) {\n        setPreference(CREATE_BACKUPS, Boolean.toString(createBackups));\n    }\n\n    int getRetainedBackupLimit() {\n        final String result = getPreference(MAX_BACKUPS);\n\n        if (result != null) {\n            return Integer.parseInt(result);\n        }\n\n        return MAX_BACKUPS_DEFAULT;\n    }\n\n    void setRetainedBackupLimit(final int retainedBackupLimit) {\n        setPreference(MAX_BACKUPS, Integer.toString(retainedBackupLimit));\n    }\n\n    boolean removeOldBackups() {\n        final String result = getPreference(REMOVE_BACKUPS);\n\n        return result == null || Boolean.parseBoolean(result);\n    }\n\n    void setRemoveOldBackups(final boolean removeOldBackups) {\n        setPreference(REMOVE_BACKUPS, Boolean.toString(removeOldBackups));\n    }\n\n    void setLastSecuritiesUpdateTimestamp(@NotNull final LocalDateTime localDateTime) {\n        setPreference(LAST_SECURITIES_UPDATE_TIMESTAMP, localDateTime.toString());\n    }\n\n    @NotNull\n    LocalDateTime getLastSecuritiesUpdateTimestamp() {\n        final String result = getPreference(LAST_SECURITIES_UPDATE_TIMESTAMP);\n\n        if (result != null) {\n            return LocalDateTime.parse(result);\n        }\n\n        return LocalDateTime.MIN;\n    }\n\n    /**\n     * Required by XStream for proper initialization.\n     *\n     * @return Properly initialized Config object\n     */\n    protected Object readResolve() {\n        postLoad();\n        return this;\n    }\n\n    @PostLoad\n    private void postLoad() {\n        preferencesLock = new ReentrantReadWriteLock(true);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/CurrencyNode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.util.logging.Logger;\n\nimport javax.persistence.Entity;\n\n/**\n * Class for representing currency nodes.\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic class CurrencyNode extends CommodityNode {\n\n    private transient ExchangeRateDAO exchangeRateDAO;\n\n    public CurrencyNode() {\n    }\n\n    /**\n     * Returns the {@code ExchangeRateDAO}.\n     *\n     * @return the exchangeRateStore\n     */\n    synchronized private ExchangeRateDAO getExchangeRateDAO() {\n        return exchangeRateDAO;\n    }\n\n    /**\n     * Sets the {@code ExchangeRateDAO}.\n     *\n     * @param exchangeRateStore the exchangeRateStore to set\n     */\n    synchronized void setExchangeRateDAO(final ExchangeRateDAO exchangeRateStore) {\n        this.exchangeRateDAO = exchangeRateStore;\n    }\n\n    /**\n     * Returns an exchange rate given a currency to convert to.\n     * \n     * @param exchangeCurrency currency to convert to\n     * @return exchange rate\n     */\n    synchronized public BigDecimal getExchangeRate(final CurrencyNode exchangeCurrency) {\n\n        if (exchangeCurrency == null) {\n            Logger.getLogger(CurrencyNode.class.getName()).severe(\"exchangeCurrency was null\");\n            return BigDecimal.ONE;\n        }\n\n        if (exchangeCurrency.equals(this)) {\n            return BigDecimal.ONE;\n        }\n\n        BigDecimal rate = getExchangeRateDAO().getExchangeRateNode(this, exchangeCurrency).getRate();\n\n        if (getSymbol().compareToIgnoreCase(exchangeCurrency.getSymbol()) < 0) {\n            rate = BigDecimal.ONE.divide(rate, MathConstants.mathContext);\n        }\n\n        return rate;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/DataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\nimport java.util.function.DoubleConsumer;\n\nimport jgnash.util.NotNull;\n\n/**\n * Interface for data storage backends.\n *\n * @author Craig Cavanaugh\n */\npublic interface DataStore {\n\n    /**\n     * Close the engine instance if open.\n     */\n    void closeEngine();\n\n    /**\n     * Create an engine instance connected to a remote server.\n     * \n     * @param host host name or IP address\n     * @param port connection port\n     * @param password user password\n     * @param engineName unique name to give the engine instance\n     * @return Engine instance if a successful connection is made\n     */\n    Engine getClientEngine(final String host, final int port, final char[] password, final String engineName);\n\n    /**\n     * Create an engine instance that uses a file.\n     * \n     * @param fileName full path to the file\n     * @param engineName unique name to give the engine instance\n     * @param password user password\n     * @return Engine instance.  A new file will be created if it does not exist\n     */\n    Engine getLocalEngine(final String fileName, final String engineName, final char[] password);\n\n    /**\n     * Returns the default file extension for this DataStore.\n     * \n     * @return file extension\n     */\n    @NotNull\n    String getFileExt();\n\n    /**\n     * Returns the full path to the file the DataStore is using.\n     * \n     * @return  full path to the file, null if this is a remotely connected DataStore\n     */\n    String getFileName();\n\n    /**\n     * Returns this DataStores type.\n     *\n     * @return type of data store\n     */\n    DataStoreType getType();\n\n    /**\n     * Local / Remote connection indicator.\n     * \n     * @return false if connected to a remote server\n     */\n    boolean isLocal();\n\n    /**\n     * Saves a Collection of StoredObjects to a file other than what is currently open.\n     * <p> \n     * The currently open file will not be closed.\n     *  @param path full path to the file to save the database to\n     * @param objects Collection of StoredObjects to save\n     * @param percentComplete callback to report the percent complete\n     */\n    void saveAs(Path path, Collection<StoredObject> objects, DoubleConsumer percentComplete);\n\n    /**\n     * Renames a datastore.\n     *\n     * @param fileName name of the datastore to rename\n     * @param newFileName the new filename\n     * @throws java.io.IOException if an I/O error occurs\n     */\n    default void rename(final String fileName, final String newFileName) throws IOException {\n        final Path path = Paths.get(fileName);\n\n        if (Files.exists(path)) {\n            Files.move(path, Paths.get(newFileName));\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/DataStoreType.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.engine.jpa.JpaH2DataStore;\nimport jgnash.engine.jpa.JpaH2MvDataStore;\nimport jgnash.engine.jpa.JpaHsqlDataStore;\nimport jgnash.engine.xstream.BinaryXStreamDataStore;\nimport jgnash.engine.xstream.XMLDataStore;\nimport jgnash.resource.util.ResourceUtils;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Storage type enumeration.\n *\n * @author Craig Cavanaugh\n */\npublic enum DataStoreType {\n\n    BINARY_XSTREAM(\n            ResourceUtils.getString(\"DataStoreType.Bxds\"),\n            false,\n            BinaryXStreamDataStore.class),\n    H2_DATABASE (\n            ResourceUtils.getString(\"DataStoreType.H2\") + \" (1.3)\",\n            true,\n            JpaH2DataStore.class),\n    H2MV_DATABASE (\n            ResourceUtils.getString(\"DataStoreType.H2\") + \" (1.4)\",\n            true,\n            JpaH2MvDataStore.class),\n    HSQL_DATABASE (\n            ResourceUtils.getString(\"DataStoreType.HSQL\"),\n            true,\n            JpaHsqlDataStore.class),\n    XML(\n            ResourceUtils.getString(\"DataStoreType.XML\"),\n            false,\n            XMLDataStore.class);\n\n\n    /* If true, then this DataStoreType can support remote connections */\n    public final transient boolean supportsRemote;\n\n    private final transient String description;\n\n    private final transient Class<? extends DataStore> dataStore;\n\n    DataStoreType(final String description, final boolean supportsRemote, final Class<? extends DataStore> dataStore) {\n        this.description = description;\n        this.supportsRemote = supportsRemote;\n        this.dataStore = dataStore;\n    }\n\n    public DataStore getDataStore() {\n        try {\n            return dataStore.getDeclaredConstructor().newInstance();\n        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException\n                | NoSuchMethodException | SecurityException ex) {\n            Logger.getLogger(DataStoreType.class.getName()).log(Level.SEVERE, null, ex);\n\n            throw new RuntimeException(ex);\n        }\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/DefaultCurrencies.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.text.DecimalFormatSymbols;\nimport java.text.NumberFormat;\nimport java.util.Currency;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Static methods for currency generation and discovery.\n * <p>\n * These are known to not show up because Java 1.4.2 and older does not have\n * a default NumberFormat defined for the currency:\n * <p>\n * {@code\n * \"SGD\"\n * \"MYR\"\n * }\n *\n * @author Craig Cavanaugh\n */\npublic class DefaultCurrencies {\n\n    /**\n     * Private Constructor, use static methods only.\n     */\n    private DefaultCurrencies() {\n    }\n\n    /**\n     * Generates an array of default currency nodes that Java knows about.\n     *\n     * @return An array of default CurrencyNodes\n     */\n    public static Set<CurrencyNode> generateCurrencies() {\n        TreeSet<CurrencyNode> set = new TreeSet<>();\n\n        for (Locale locale : NumberFormat.getAvailableLocales()) {\n\n            // only try if a valid county length is returned\n            if (locale.getCountry().length() == 2) {\n                try {\n                    if (Currency.getInstance(locale) != null) {\n                        set.add(buildNode(locale));\n                    }\n                } catch (final IllegalArgumentException ignored) {\n                    // ignored, locale is not a supported ISO 3166 country code\n                } catch (final Exception ex) {\n                    Logger.getLogger(DefaultCurrencies.class.getName()).log(Level.SEVERE, null, ex);\n                }\n            }\n        }\n\n        return set;\n    }\n\n    /**\n     * Creates a custom CurrencyNode given an ISO code.  If the ISO code is\n     * not valid, then a node will be generated using the supplied code.  The\n     * locale is assumed to be the default locale.\n     *\n     * @param ISOCode The custom currency to generate\n     * @return The custom CurrencyNode\n     */\n    public static CurrencyNode buildCustomNode(final String ISOCode) {\n        final CurrencyNode node = new CurrencyNode();\n        Currency c;\n\n        try {\n            c = Currency.getInstance(ISOCode);\n            node.setSymbol(c.getCurrencyCode());\n        } catch (Exception e) {\n            node.setSymbol(ISOCode);\n            Logger.getLogger(DefaultCurrencies.class.getName()).log(Level.FINE, null, e);\n        } finally {\n            node.setDescription(Locale.getDefault().toString());\n        }\n        return node;\n    }\n\n    /**\n     * Creates a valid CurrencyNode given a locale.\n     *\n     * @param locale Locale to create a CurrencyNode for\n     * @return The new CurrencyNode\n     */\n    public static CurrencyNode buildNode(final Locale locale) {\n        DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);\n        Currency c = symbols.getCurrency();\n\n        CurrencyNode node = new CurrencyNode();\n        node.setSymbol(c.getCurrencyCode());\n        node.setPrefix(symbols.getCurrencySymbol());\n\n        byte scale = (byte) c.getDefaultFractionDigits();\n\n        if (scale == -1) {  // The JVM may return a negative value for some Locales\n            scale = 0;      // scale may be -1, but this is not allowed for CurrencyNodes\n        }\n\n        node.setScale(scale);\n\n        return node;\n    }\n\n    /**\n     * Generates the default CurrencyNode for the current locale.\n     *\n     * @return The new CurrencyNode\n     */\n    public static CurrencyNode getDefault() {\n        try {\n            return buildNode(Locale.getDefault());\n        } catch (final Exception e) {\n            return buildNode(Locale.US);\n        }\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/Engine.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.file.Path;\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletionService;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.RejectedExecutionException;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.function.Function;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport jgnash.engine.attachment.AttachmentManager;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.budget.BudgetGoal;\nimport jgnash.engine.concurrent.LockManager;\nimport jgnash.engine.dao.AccountDAO;\nimport jgnash.engine.dao.BudgetDAO;\nimport jgnash.engine.dao.CommodityDAO;\nimport jgnash.engine.dao.ConfigDAO;\nimport jgnash.engine.dao.EngineDAO;\nimport jgnash.engine.dao.RecurringDAO;\nimport jgnash.engine.dao.TransactionDAO;\nimport jgnash.engine.dao.TrashDAO;\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.engine.recurring.MonthlyReminder;\nimport jgnash.engine.recurring.PendingReminder;\nimport jgnash.engine.recurring.RecurringIterator;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.net.currency.CurrencyUpdateFactory;\nimport jgnash.net.security.UpdateFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\nimport org.apache.commons.collections4.ListUtils;\n\n/**\n * Engine class\n * <p>\n * When objects are removed, they are wrapped in a TrashObject so they may still be referenced for messaging and cleanup\n * operations. After a predefined period of time, they are permanently removed.\n *\n * @author Craig Cavanaugh\n */\npublic class Engine {\n\n    /**\n     * Current version for the file format.\n     */\n    public static final int CURRENT_MAJOR_VERSION = 3;\n\n    public static final int CURRENT_MINOR_VERSION = 6;\n\n    // Lock name\n    private static final String BIG_LOCK = \"bigLock\";\n\n    private static final Logger logger = Logger.getLogger(Engine.class.getName());\n\n    private static final long MAXIMUM_TRASH_AGE = 2L * 60L * 1000L; // 2 minutes\n\n    /**\n     * The maximum number of network errors before scheduled tasks are stopped.\n     */\n    private static final short MAX_ERRORS = 2;\n\n    /**\n     * Time in seconds to delay start of background updates.\n     */\n    private static final int SCHEDULED_DELAY = 30;\n\n    /**\n     * Time is seconds for a forced shutdown of background services\n     */\n    private static final int FORCED_SHUTDOWN_TIMEOUT = 15;\n\n    private static final String MESSAGE_ACCOUNT_MODIFY = \"Message.AccountModify\";\n\n    private static final String COMMODITY = \"Commodity \";\n\n    static {\n        logger.setLevel(Level.ALL);\n    }\n\n    private final ResourceBundle rb = ResourceUtils.getBundle();\n\n    /**\n     * Primary lock for any operation that alters or reads data\n     */\n    private final ReentrantReadWriteLock dataLock;\n\n    private final AtomicInteger backGroundCounter = new AtomicInteger();\n    /**\n     * Named identifier for this engine instance.\n     */\n    private final String name;\n    /**\n     * Unique identifier for this engine instance.\n     * Used by this distributed lock manager to keep track of who has a lock\n     */\n    private final String uuid = UUID.randomUUID().toString();\n\n    private final EngineDAO eDAO;\n\n    private final AttachmentManager attachmentManager;\n\n    /**\n     * Background executor service for trash management and currency / security updates\n     */\n    private final ScheduledThreadPoolExecutor backgroundExecutorService;\n\n    /**\n     * All engine instances will share the same message bus.\n     */\n    private final MessageBus messageBus;\n\n    /**\n     * Cached for performance.\n     */\n    private Config config;\n\n    /**\n     * Cached for performance.\n     */\n    private RootAccount rootAccount;\n\n    private ExchangeRateDAO exchangeRateDAO;\n\n    /**\n     * Cached for performance.\n     */\n    private String accountSeparator = null;\n\n    public Engine(final EngineDAO eDAO, final LockManager lockManager, final AttachmentManager attachmentManager, final String name) {\n        Objects.requireNonNull(name, \"The engine name may not be null\");\n        Objects.requireNonNull(eDAO, \"The engineDAO may not be null\");\n\n        logger.log(Level.INFO, \"Release {0}.{1}\", new Object[]{CURRENT_MAJOR_VERSION, CURRENT_MINOR_VERSION});\n\n        this.attachmentManager = attachmentManager;\n        this.eDAO = eDAO;\n        this.name = name;\n\n        // Generate lock\n        dataLock = lockManager.getLock(BIG_LOCK);\n\n        messageBus = MessageBus.getInstance(name);\n\n        initialize();\n\n        checkAndCorrect();\n\n        backgroundExecutorService = new ScheduledThreadPoolExecutor(1,\n                new DefaultDaemonThreadFactory(\"Engine Background Executor\"));\n        backgroundExecutorService.setRemoveOnCancelPolicy(true);\n        backgroundExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);\n\n        // run trash cleanup every 5 minutes 45 seconds after startup\n        backgroundExecutorService.scheduleWithFixedDelay(() -> {\n            if (!Thread.currentThread().isInterrupted()) {\n                emptyTrash();\n            }\n        }, 45, 5L * 60L, TimeUnit.SECONDS);\n\n        backgroundExecutorService.schedule(() -> {\n            if (UpdateFactory.getUpdateOnStartup()) {\n                // don't update on weekends unless needed\n                if (UpdateFactory.shouldAutomaticUpdateOccur(getConfig().getLastSecuritiesUpdateTimestamp())) {\n                    startSecuritiesUpdate(SCHEDULED_DELAY);\n                }\n            }\n        }, 30, TimeUnit.SECONDS);\n\n        backgroundExecutorService.schedule(() -> {\n            if (CurrencyUpdateFactory.getUpdateOnStartup()) {\n                startExchangeRateUpdate(SCHEDULED_DELAY);\n            }\n        }, 30, TimeUnit.SECONDS);\n    }\n\n    boolean isFileDirty() {\n        return eDAO.isDirty() || getAccountDAO().isDirty() || getBudgetDAO().isDirty() || getCommodityDAO().isDirty()\n                       || getConfigDAO().isDirty() || getReminderDAO().isDirty() || getTransactionDAO().isDirty()\n                       || getTrashDAO().isDirty();\n    }\n\n    /**\n     * Registers a {@code Handler} with the class logger.\n     * This also ensures the static logger is initialized.\n     *\n     * @param handler {@code Handler} to register\n     */\n    public static void addLogHandler(final Handler handler) {\n        logger.addHandler(handler);\n    }\n\n    /**\n     * Returns the most current known market price for a requested date.  The {@code SecurityNode} history will be\n     * searched for an exact match first.  If an exact match is not found, investment transactions will be searched\n     * for the closest requested date.  {@code SecurityHistoryNode} history values will take precedent over\n     * a transaction with the same closest or matching date.\n     *\n     * @param transactions Collection of transactions utilizing the requested investment\n     * @param node         {@code SecurityNode} we want a price for\n     * @param baseCurrency {@code CurrencyNode} reporting currency\n     * @param localDate    {@code LocalDate} we want a market price for\n     * @return The best market price or a value of 0 if no history or transactions exist\n     */\n    public static BigDecimal getMarketPrice(final Collection<Transaction> transactions, final SecurityNode node,\n                                            final CurrencyNode baseCurrency, final LocalDate localDate) {\n\n        // Search for the exact history node record\n        Optional<SecurityHistoryNode> optional = node.getHistoryNode(localDate);\n\n        // not null, must be an exact match, return the value because it has precedence\n        if (optional.isPresent()) {\n            return node.getMarketPrice(localDate, baseCurrency);\n        }\n\n        // Nothing found yet, continue searching for something better\n        LocalDate priceDate = LocalDate.ofEpochDay(0);\n        BigDecimal price = BigDecimal.ZERO;\n\n        optional = node.getClosestHistoryNode(localDate);\n\n        if (optional.isPresent()) {    // Closest option so far\n            price = optional.get().getPrice();\n            priceDate = optional.get().getLocalDate();\n        }\n\n        // Compare against transactions\n        for (final Transaction t : transactions) {\n            if (t instanceof InvestmentTransaction && ((InvestmentTransaction) t).getSecurityNode() == node) {\n\n                // The transaction date must be closer than the history node, but not newer than the request date\n                if ((t.getLocalDate().isAfter(priceDate) && t.getLocalDate().isBefore(localDate)) || t.getLocalDate().equals(localDate)) {\n\n                    // Check for a dividend, etc that may have returned a price of zero\n                    final BigDecimal p = ((InvestmentTransaction) t).getPrice();\n\n                    if (p != null && p.compareTo(BigDecimal.ZERO) > 0) {\n                        price = p;\n                        priceDate = t.getLocalDate();\n                    }\n                }\n            }\n        }\n\n        // Get the current exchange rate for the security node\n        final BigDecimal rate = node.getReportedCurrencyNode().getExchangeRate(baseCurrency);\n\n        // return the price and factor in the exchange rate\n        return price.multiply(rate);\n    }\n\n    static String buildExchangeRateId(final CurrencyNode baseCurrency, final CurrencyNode exchangeCurrency) {\n\n        String rateId;\n        if (baseCurrency.getSymbol().compareToIgnoreCase(exchangeCurrency.getSymbol()) > 0) {\n            rateId = baseCurrency.getSymbol() + exchangeCurrency.getSymbol();\n        } else {\n            rateId = exchangeCurrency.getSymbol() + baseCurrency.getSymbol();\n        }\n        return rateId;\n    }\n\n    /**\n     * Returns the engine logger.\n     *\n     * @return the engine logger\n     */\n    public static Logger getLogger() {\n        return logger;\n    }\n\n    /**\n     * Log a informational message.\n     *\n     * @param message message to display\n     */\n    private static void logInfo(final String message) {\n        logger.log(Level.INFO, message);\n    }\n\n    /**\n     * Log a warning message.\n     *\n     * @param message message to display\n     */\n    private static void logWarning(final String message) {\n        logger.warning(message);\n    }\n\n    /**\n     * Log a severe message.\n     *\n     * @param message message to display\n     */\n    private static void logSevere(final String message) {\n        logger.severe(message);\n    }\n\n    private static void shutDownAndWait(final ExecutorService executorService) {\n        executorService.shutdownNow();\n\n        try {\n            if (!executorService.awaitTermination(FORCED_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) {\n\n                if (!executorService.awaitTermination(FORCED_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) {\n                    logSevere(\"Unable to shutdown background service\");\n                }\n            }\n\n        } catch (final InterruptedException e) {\n            executorService.shutdownNow();\n            Thread.currentThread().interrupt();\n\n            logger.log(Level.FINEST, e.getLocalizedMessage(), e);\n        }\n    }\n\n    /**\n     * Initiates a background exchange rate update with a given start delay.\n     *\n     * @param delay delay in seconds\n     */\n    public void startExchangeRateUpdate(final int delay) {\n        backgroundExecutorService.schedule(new BackgroundCallable(new CurrencyUpdateFactory.UpdateExchangeRatesCallable()), delay,\n                TimeUnit.SECONDS);\n    }\n\n    /**\n     * Initiates a background securities history update with a given start delay.\n     *\n     * @param delay delay in seconds\n     */\n    public void startSecuritiesUpdate(final int delay) {\n        final List<BackgroundCallable> callables = new ArrayList<>();\n\n        getSecurities().stream().filter(securityNode ->\n                                                securityNode.getQuoteSource() != QuoteSource.NONE).forEach(securityNode -> { // failure will occur if source is not defined\n\n            callables.add(new BackgroundCallable(new UpdateFactory.UpdateSecurityNodeCallable(securityNode)));\n            callables.add(new BackgroundCallable(new UpdateFactory.UpdateSecurityNodeEventsCallable(securityNode)));\n        });\n\n        // Cleanup thread that monitors for excess network connection failures\n        new SecuritiesUpdateRunnable(callables, delay).start();\n\n        // Save the last update\n        config.setLastSecuritiesUpdateTimestamp(LocalDateTime.now());\n    }\n\n    /**\n     * Creates a RootAccount and default currency only if necessary.\n     */\n    private void initialize() {\n\n        dataLock.writeLock().lock();\n\n        try {\n\n            // ask the Config object to perform any needed configuration\n            getConfig().initialize();\n\n            // build the exchange rate storage object\n            exchangeRateDAO = new ExchangeRateDAO(getCommodityDAO());\n\n            // assign the exchange rate store to the currencies\n            for (final CurrencyNode node : getCurrencies()) {\n                node.setExchangeRateDAO(exchangeRateDAO);\n            }\n\n            // obtain or establish the root account\n            RootAccount root = getRootAccount();\n\n            if (root == null) {\n                CurrencyNode node = getDefaultCurrency();\n\n                if (node == null) {\n                    node = DefaultCurrencies.getDefault();\n                    node.setExchangeRateDAO(exchangeRateDAO);\n\n                    addCurrency(node); // force the node to persisted\n                }\n\n                root = new RootAccount(node);\n                root.setName(rb.getString(\"Name.Root\"));\n                root.setDescription(rb.getString(\"Name.Root\"));\n\n                logInfo(\"Creating RootAccount\");\n\n                if (!getAccountDAO().addRootAccount(root)) {\n                    logSevere(\"Was not able to add the root account\");\n                    throw new EngineException(\"Was not able to add the root account\");\n                }\n\n                if (getDefaultCurrency() == null) {\n                    setDefaultCurrency(node);\n                }\n            }\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n\n        logInfo(\"Engine initialization is complete\");\n    }\n\n    /**\n     * Corrects minor issues with a database that may occur because of prior bugs or file format upgrades.\n     */\n    private void checkAndCorrect() {\n        dataLock.writeLock().lock();\n\n        try {\n            // check and correct multiple root accounts from old files... there are still a few.\n            List<Account> accountList = getAccountList().stream().filter(account -> account.getAccountType()\n                                                                                            .equals(AccountType.ROOT)).collect(Collectors.toList());\n\n            if (accountList.size() > 1) {\n                for (Account account : accountList) {\n                    if (account.getChildCount() == 0) {\n                        removeAccount(account);\n                        logWarning(\"Removed an extra / empty root account\");\n                    }\n                }\n            }\n\n            final List<Config> list = eDAO.getStoredObjects(Config.class);\n            if (list.size() > 1) {\n                // Delete all but the first found config object\n                for (int i = 1; i < list.size(); i++) {\n                    logWarning(\"Removed an extra Config object\");\n                    moveObjectToTrash(list.get(i));\n                }\n            }\n\n            // Transaction timestamps were updated for release 2.25\n            if (getConfig().getMinorFileFormatVersion() < 25 && getConfig().getMajorFileFormatVersion() < 3) {\n                // Update transactions in chunks of 200\n                ListUtils.partition(getTransactions(), 200).forEach(eDAO::bulkUpdate);\n            }\n\n            // update the file version if it is not current\n            if (getConfig().getMajorFileFormatVersion() != CURRENT_MAJOR_VERSION\n                        || getConfig().getMinorFileFormatVersion() != CURRENT_MINOR_VERSION) {\n\n                final Config localConfig = getConfig();\n                localConfig.updateFileVersion();\n                getConfigDAO().update(localConfig);\n            }\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    private void clearObsoleteExchangeRates() {\n        getCommodityDAO().getExchangeRates().stream()\n                .filter(rate -> getBaseCurrencies(rate.getRateId()).length == 0)\n                .forEach(this::removeExchangeRate);\n    }\n\n    private void removeExchangeRate(final ExchangeRate rate) {\n        dataLock.writeLock().lock();\n\n        try {\n            for (final ExchangeRateHistoryNode node : rate.getHistory()) {\n                removeExchangeRateHistory(rate, node);\n            }\n            moveObjectToTrash(rate);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    void stopBackgroundServices() {\n        logInfo(\"Controlled engine shutdown initiated\");\n\n        shutDownAndWait(backgroundExecutorService);\n\n        logInfo(\"Background services have been stopped\");\n    }\n\n    void shutdown() {\n        eDAO.shutdown();\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    private AccountDAO getAccountDAO() {\n        return eDAO.getAccountDAO();\n    }\n\n    private BudgetDAO getBudgetDAO() {\n        return eDAO.getBudgetDAO();\n    }\n\n    private CommodityDAO getCommodityDAO() {\n        return eDAO.getCommodityDAO();\n    }\n\n    private ConfigDAO getConfigDAO() {\n        return eDAO.getConfigDAO();\n    }\n\n    private RecurringDAO getReminderDAO() {\n        return eDAO.getRecurringDAO();\n    }\n\n    private TransactionDAO getTransactionDAO() {\n        return eDAO.getTransactionDAO();\n    }\n\n    private TrashDAO getTrashDAO() {\n        return eDAO.getTrashDAO();\n    }\n\n    private boolean moveObjectToTrash(final Object object) {\n        boolean result = false;\n\n        dataLock.writeLock().lock();\n\n        try {\n            if (object instanceof StoredObject) {\n                getTrashDAO().add(new TrashObject((StoredObject) object));\n            } else {    // simple object with an annotated JPA entity id of type long is assumed\n                getTrashDAO().addEntityTrash(object);\n            }\n            result = true;\n        } catch (final Exception ex) {\n            logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n\n        return result;\n    }\n\n    /**\n     * Empty the trash if any objects are older than the defined time.\n     */\n    private void emptyTrash() {\n        if (backGroundCounter.incrementAndGet() == 1) {\n            messageBus.fireEvent(new Message(MessageChannel.SYSTEM, ChannelEvent.BACKGROUND_PROCESS_STARTED,\n                    Engine.this));\n        }\n\n        dataLock.writeLock().lock();\n\n        try {\n            logger.info(\"Checking for trash\");\n\n            final List<TrashObject> trash = getTrashDAO().getTrashObjects();\n\n            /* always sort by the timestamp of the trash object to prevent\n             * foreign key removal exceptions when multiple related accounts\n             * or objects are removed */\n            Collections.sort(trash);\n\n            if (trash.isEmpty()) {\n                logger.info(\"No trash was found\");\n            }\n\n            trash.stream().filter(o -> ChronoUnit.MILLIS.between(o.getDate(), LocalDateTime.now()) >= MAXIMUM_TRASH_AGE)\n                    .forEach(o -> getTrashDAO().remove(o));\n        } finally {\n            dataLock.writeLock().unlock();\n\n            if (backGroundCounter.decrementAndGet() == 0) {\n                messageBus.fireEvent(new Message(MessageChannel.SYSTEM, ChannelEvent.BACKGROUND_PROCESS_STOPPED,\n                        Engine.this));\n            }\n        }\n    }\n\n    /**\n     * Creates a default reminder given a transaction and the primary account.  The Reminder will need to persisted.\n     *\n     * @param transaction Transaction for the reminder.  The transaction will be cloned\n     * @param account     primary account\n     * @return new default {@code MonthlyReminder}\n     */\n    public static Reminder createDefaultReminder(final Transaction transaction, final Account account) {\n        final Reminder reminder = new MonthlyReminder();\n\n        try {\n            reminder.setAccount(account);\n            reminder.setStartDate(transaction.getLocalDate().plusMonths(1));\n            reminder.setTransaction((Transaction) transaction.clone());\n            reminder.setDescription(transaction.getPayee());\n            reminder.setNotes(transaction.getMemo());\n        } catch (final CloneNotSupportedException e) {\n            logSevere(e.getLocalizedMessage());\n        }\n        return reminder;\n    }\n\n    public boolean addReminder(final Reminder reminder) {\n        Objects.requireNonNull(reminder.getUuid());\n\n        boolean result = false;\n\n        // make sure the description has been set\n        if (reminder.getDescription() != null && !reminder.getDescription().isBlank()) {\n            result = getReminderDAO().addReminder(reminder);\n        }\n\n        Message message;\n        if (result) {\n            message = new Message(MessageChannel.REMINDER, ChannelEvent.REMINDER_ADD, this);\n        } else {\n            message = new Message(MessageChannel.REMINDER, ChannelEvent.REMINDER_ADD_FAILED, this);\n        }\n\n        message.setObject(MessageProperty.REMINDER, reminder);\n        messageBus.fireEvent(message);\n\n        return result;\n    }\n\n    public boolean removeReminder(final Reminder reminder) {\n        boolean result = false;\n\n        if (moveObjectToTrash(reminder)) {\n\n            if (reminder.getTransaction() != null) {\n                moveObjectToTrash(reminder.getTransaction());\n                reminder.setTransaction(null);\n            }\n\n            Message message = new Message(MessageChannel.REMINDER, ChannelEvent.REMINDER_REMOVE, this);\n\n            message.setObject(MessageProperty.REMINDER, reminder);\n            messageBus.fireEvent(message);\n\n            result = true;\n        }\n\n        return result;\n    }\n\n    /**\n     * Returns a list of reminders.\n     *\n     * @return List of reminders\n     */\n    public List<Reminder> getReminders() {\n        return getReminderDAO().getReminderList();\n    }\n\n    public Reminder getReminderByUuid(final UUID uuid) {\n        return getReminderDAO().getReminderByUuid(uuid);\n    }\n\n    public List<PendingReminder> getPendingReminders() {\n        final ArrayList<PendingReminder> pendingList = new ArrayList<>();\n        final List<Reminder> list = getReminders();\n        final LocalDate now = LocalDate.now(); // today's date\n\n        for (final Reminder r : list) {\n            if (r.isEnabled()) {\n                final RecurringIterator ri = r.getIterator();\n                LocalDate next = ri.next();\n\n                while (next != null) {\n                    LocalDate date = next;\n\n                    if (r.isAutoCreate()) {\n                        date = date.minusDays(r.getDaysAdvance());\n                    }\n\n                    if (DateUtils.before(date, now)) { // need to fire this reminder\n                        pendingList.add(new PendingReminder(r, next));\n                        next = ri.next();\n                    } else {\n                        next = null;\n                    }\n                }\n            }\n        }\n\n        return pendingList;\n    }\n\n    public static PendingReminder getPendingReminder(@NotNull Reminder reminder) {\n        final RecurringIterator ri = reminder.getIterator();\n        LocalDate next = ri.next();\n\n        if (next != null) {\n            return new PendingReminder(reminder, next);\n        }\n\n        return null;\n    }\n\n    public void processPendingReminders(final Collection<PendingReminder> pendingReminders) {\n        pendingReminders.stream().filter(PendingReminder::isApproved).forEach(pending -> {\n            final Reminder reminder = pending.getReminder();\n\n            if (reminder.getTransaction() != null) { // add the transaction\n                final Transaction t = reminder.getTransaction();\n\n                // Update to the commit date (commit date can be modified)\n                t.setDate(pending.getCommitDate());\n                addTransaction(t);\n            }\n            // update the last fired date... date returned from the iterator\n            reminder.setLastDate(); // mark as complete\n            if (!updateReminder(reminder)) {\n                logSevere(rb.getString(\"Message.Error.ReminderUpdate\"));\n            }\n        });\n    }\n\n    public <T extends StoredObject> T getStoredObjectByUuid(final Class<T> tClass, final UUID uuid) {\n        return eDAO.getObjectByUuid(tClass, uuid);\n    }\n\n    /**\n     * Returns a {@code Collection} of all {@code StoredObjects} in a consistent order.\n     * {@code StoredObjects} marked for removal and {@code TrashObjects} are filtered from the collection.\n     *\n     * @return {@code Collection} of {@code StoredObjects}\n     * @see Collection\n     * @see StoredObjectComparator\n     */\n    public Collection<StoredObject> getStoredObjects() {\n        dataLock.readLock().lock();\n\n        try {\n\n            List<StoredObject> objects = eDAO.getStoredObjects();\n\n            // Filter out objects to be removed\n            objects.removeIf(TrashObject.class::isInstance);\n\n            objects.sort(new StoredObjectComparator());\n\n            return objects;\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Validate a CommodityNode for correctness.\n     *\n     * @param node CommodityNode to validate\n     * @return true if valid\n     */\n    private boolean isCommodityNodeValid(final CommodityNode node) {\n        boolean result = true;\n\n        if (node.getUuid() == null) {\n            result = false;\n            logSevere(\"Commodity uuid was not valid\");\n        }\n\n        if (node.getSymbol() == null || node.getSymbol().isEmpty()) {\n            result = false;\n            logSevere(\"Commodity symbol was not valid\");\n        }\n\n        if (node.getScale() < 0) {\n            result = false;\n            logSevere(COMMODITY + node + \" had a scale less than zero\");\n        }\n\n        if (node instanceof SecurityNode && ((SecurityNode) node).getReportedCurrencyNode() == null) {\n            result = false;\n            logSevere(COMMODITY + node + \" was not assigned a currency\");\n        }\n\n        // ensure the UUID being used is unique\n        if (eDAO.getObjectByUuid(CommodityNode.class, node.getUuid()) != null) {\n            result = false;\n            logSevere(COMMODITY + node + \" was not unique\");\n        }\n\n        return result;\n    }\n\n    /**\n     * Adds a new CurrencyNode to the data set.\n     * <p>\n     * Checks and prevents the addition of a duplicate Currencies.\n     *\n     * @param node new CurrencyNode to add\n     * @return {@code true} if the add it successful\n     */\n    public boolean addCurrency(final CurrencyNode node) {\n        dataLock.writeLock().lock();\n\n        try {\n            boolean status = isCommodityNodeValid(node);\n\n            if (status) {\n                node.setExchangeRateDAO(exchangeRateDAO);\n\n                if (getCurrency(node.getSymbol()) != null) {\n                    logger.log(Level.INFO, \"Prevented addition of a duplicate CurrencyNode: {0}\", node.getSymbol());\n                    status = false;\n                }\n            }\n\n            if (status) {\n                status = getCommodityDAO().addCommodity(node);\n                logger.log(Level.FINE, \"Adding: {0}\", node);\n            }\n\n            Message message;\n            if (status) {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.CURRENCY_ADD, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.CURRENCY_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Links the {@code CurrencyNode} to the exchange DAO.\n     * Support method to be used during import operations\n     *\n     * @param currencyNode {@code CurrencyNode} to link\n     */\n    void attachCurrencyNode(final CurrencyNode currencyNode) {\n        currencyNode.setExchangeRateDAO(exchangeRateDAO);\n    }\n\n    /**\n     * Adds a new SecurityNode to the data set.\n     * <p>\n     * Checks and prevents the addition of a duplicate SecurityNode.\n     *\n     * @param node new SecurityNode to add\n     * @return {@code true} if the add it successful\n     */\n    public boolean addSecurity(final SecurityNode node) {\n        dataLock.writeLock().lock();\n\n        try {\n            boolean status = isCommodityNodeValid(node);\n\n            if (status) {\n                if (getSecurity(node.getSymbol()) != null) {\n                    logger.log(Level.INFO, \"Prevented addition of a duplicate SecurityNode: {0}\", node.getSymbol());\n                    status = false;\n                }\n            }\n\n            if (status) {\n                status = getCommodityDAO().addCommodity(node);\n                logger.log(Level.FINE, \"Adding: {0}\", node);\n            }\n\n            Message message;\n            if (status) {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_ADD, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Add a SecurityHistoryNode node to a SecurityNode.  If the SecurityNode already contains\n     * an equivalent SecurityHistoryNode, the old SecurityHistoryNode is removed first.\n     *\n     * @param node  SecurityNode to add to\n     * @param hNode SecurityHistoryNode to add\n     * @return <tt>true</tt> if successful\n     */\n    public boolean addSecurityHistory(@NotNull final SecurityNode node, @NotNull final SecurityHistoryNode hNode) {\n        dataLock.writeLock().lock();\n\n        try {\n            // Remove old history of the same date if it exists\n            if (node.contains(hNode.getLocalDate())) {\n                if (!removeSecurityHistory(node, hNode.getLocalDate())) {\n                    logSevere(ResourceUtils.getString(\"Message.Error.HistRemoval\", hNode.getLocalDate(), node.getSymbol()));\n                    return false;\n                }\n            }\n\n            boolean status = node.addHistoryNode(hNode);\n\n            if (status) {\n                status = getCommodityDAO().addSecurityHistory(node, hNode);\n            }\n\n            Message message;\n\n            if (status) {\n                clearCachedAccountBalance(node);\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_ADD, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Add a SecurityHistoryNode node to a SecurityNode.  If the SecurityNode already contains\n     * an equivalent SecurityHistoryNode, the old SecurityHistoryNode is removed first.\n     *\n     * @param node         SecurityNode to add to\n     * @param historyEvent SecurityHistoryNode to add\n     * @return <tt>true</tt> if successful\n     */\n    public boolean addSecurityHistoryEvent(@NotNull final SecurityNode node, @NotNull final SecurityHistoryEvent historyEvent) {\n        dataLock.writeLock().lock();\n\n        try {\n\n            // Remove old history event if it exists, equality is used to work around hibernate optimizations\n            // A defensive copy of the old events is used to prevent concurrent modification errors\n            new HashSet<>(node.getHistoryEvents()).stream().filter(event -> event.equals(historyEvent))\n                    .forEach(event -> removeSecurityHistoryEvent(node, historyEvent));\n\n            boolean status = node.addSecurityHistoryEvent(historyEvent);\n\n            if (status) {\n                status = getCommodityDAO().addSecurityHistoryEvent(node, historyEvent);\n            }\n\n            Message message;\n\n            if (status) {\n                clearCachedAccountBalance(node);\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_EVENT_ADD, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_EVENT_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a list of investment accounts that use the given security node.\n     *\n     * @param node security node\n     * @return list of investment accounts\n     */\n    private Set<Account> getInvestmentAccountList(final SecurityNode node) {\n        return getInvestmentAccountList().parallelStream()\n                       .filter(account -> account.containsSecurity(node)).collect(Collectors.toSet());\n    }\n\n    /**\n     * Forces all investment accounts containing the security to clear the cached account balance and reconciled account\n     * balance and recalculate when queried.\n     *\n     * @param node SecurityNode that was changed\n     */\n    private void clearCachedAccountBalance(final SecurityNode node) {\n        getInvestmentAccountList(node).forEach(this::clearCachedAccountBalance);\n    }\n\n    /**\n     * Clears an {@code Accounts} cached balance and recursively works up the tree to the root.\n     *\n     * @param account {@code Account} to clear\n     */\n    private void clearCachedAccountBalance(final Account account) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            account.clearCachedBalances();\n\n            // force a persistence update if working as a client / server\n            if (eDAO.isRemote()) {\n                getAccountDAO().updateAccount(account);\n            }\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n\n        if (account.getParent() != null && account.getParent().getAccountType() != AccountType.ROOT) {\n            clearCachedAccountBalance(account.getParent());\n        }\n    }\n\n    private CurrencyNode[] getBaseCurrencies(final String exchangeRateId) {\n        dataLock.readLock().lock();\n\n        try {\n            final List<CurrencyNode> currencies = getCurrencies();\n\n            Collections.sort(currencies);\n            Collections.reverse(currencies);\n\n            for (final CurrencyNode node1 : currencies) {\n                for (final CurrencyNode node2 : currencies) {\n                    if (node1 != node2 && buildExchangeRateId(node1, node2).equals(exchangeRateId)) {\n                        return new CurrencyNode[]{node1, node2};\n                    }\n                }\n            }\n            return new CurrencyNode[0];\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns an array of currencies being used in accounts.\n     *\n     * @return Set of CurrencyNodes\n     */\n    public Set<CurrencyNode> getActiveCurrencies() {\n        dataLock.readLock().lock();\n\n        try {\n            return getCommodityDAO().getActiveCurrencies();\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a CurrencyNode given the symbol. This will not generate a new CurrencyNode. It must be explicitly created\n     * and added.\n     *\n     * @param symbol Currency symbol\n     * @return null if the CurrencyNode as not been defined\n     */\n    public CurrencyNode getCurrency(final String symbol) {\n        dataLock.readLock().lock();\n\n        try {\n            CurrencyNode rNode = null;\n\n            for (CurrencyNode node : getCurrencies()) {\n                if (node.getSymbol().equals(symbol)) {\n                    rNode = node;\n                    break;\n                }\n            }\n            return rNode;\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public List<CurrencyNode> getCurrencies() {\n        dataLock.readLock().lock();\n\n        try {\n            return getCommodityDAO().getCurrencies();\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public CurrencyNode getCurrencyNodeByUuid(final UUID uuid) {\n        return getCommodityDAO().getCurrencyByUuid(uuid);\n    }\n\n    public ExchangeRate getExchangeRate(final CurrencyNode baseCurrency, final CurrencyNode exchangeCurrency) {\n        dataLock.readLock().lock();\n\n        try {\n            return exchangeRateDAO.getExchangeRateNode(baseCurrency, exchangeCurrency);\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public ExchangeRate getExchangeRateByUuid(final UUID uuid) {\n        return getCommodityDAO().getExchangeRateByUuid(uuid);\n    }\n\n    @NotNull\n    public List<SecurityNode> getSecurities() {\n        dataLock.readLock().lock();\n\n        try {\n            return getCommodityDAO().getSecurities();\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Find a SecurityNode given it's symbol.\n     *\n     * @param symbol symbol of security to find\n     * @return null if not found\n     */\n    public SecurityNode getSecurity(final String symbol) {\n        dataLock.readLock().lock();\n\n        try {\n            List<SecurityNode> list = getSecurities();\n\n            SecurityNode sNode = null;\n\n            for (SecurityNode node : list) {\n                if (node.getSymbol().equals(symbol)) {\n                    sNode = node;\n                    break;\n                }\n            }\n            return sNode;\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public SecurityNode getSecurityNodeByUuid(final UUID uuid) {\n        return getCommodityDAO().getSecurityByUuid(uuid);\n    }\n\n    private boolean isCommodityNodeUsed(final CommodityNode node) {\n        dataLock.readLock().lock();\n\n        try {\n            List<Account> list = getAccountList();\n\n            for (Account a : list) {\n                if (a.getCurrencyNode().equals(node)) {\n                    return true;\n                }\n\n                if (a.getAccountType() == AccountType.INVEST || a.getAccountType() == AccountType.MUTUAL) {\n                    for (SecurityNode j : a.getSecurities()) {\n                        if (j.equals(node) || j.getReportedCurrencyNode().equals(node)) {\n                            return true;\n                        }\n                    }\n                }\n            }\n\n            List<SecurityNode> sList = getSecurities();\n\n            for (SecurityNode sNode : sList) {\n                if (sNode.getReportedCurrencyNode().equals(node)) {\n                    return true;\n                }\n            }\n\n        } finally {\n            dataLock.readLock().unlock();\n        }\n\n        return false;\n    }\n\n    public boolean removeCommodity(final CurrencyNode node) {\n        boolean status = true;\n\n        dataLock.writeLock().lock();\n\n        try {\n            if (isCommodityNodeUsed(node)) {\n                status = false;\n            } else {\n                clearObsoleteExchangeRates();\n                moveObjectToTrash(node);\n            }\n\n            Message message;\n            if (status) {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.CURRENCY_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.CURRENCY_REMOVE_FAILED, this);\n            }\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public boolean removeSecurity(final SecurityNode node) {\n        boolean status = true;\n\n        dataLock.writeLock().lock();\n\n        try {\n            if (isCommodityNodeUsed(node)) {\n                status = false;\n            } else {\n                // Remove all history nodes first so they are not left behind\n\n                // A copy is made to prevent a concurrent modification error to the underlying list, Bug #208\n                final List<SecurityHistoryNode> hNodes = new ArrayList<>(node.getHistoryNodes());\n\n                hNodes.stream()\n                        .filter(hNode -> !removeSecurityHistory(node, hNode.getLocalDate()))\n                        .forEach(hNode -> logSevere(ResourceUtils.getString(\"Message.Error.HistRemoval\",\n                                hNode.getLocalDate(), node.getSymbol())));\n                moveObjectToTrash(node);\n            }\n\n            Message message;\n            if (status) {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_REMOVE_FAILED, this);\n            }\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Remove a {@code SecurityHistoryNode} given a {@code Date}.\n     *\n     * @param node {@code SecurityNode} to remove history from\n     * @param date the search {@code Date}\n     * @return {@code true} if a {@code SecurityHistoryNode} was found and removed\n     */\n    public boolean removeSecurityHistory(@NotNull final SecurityNode node, @NotNull final LocalDate date) {\n        dataLock.writeLock().lock();\n\n        boolean status = false;\n\n        try {\n            final Optional<SecurityHistoryNode> optional = node.getHistoryNode(date);\n\n            if (optional.isPresent()) {\n                status = node.removeHistoryNode(date);\n\n                if (status) {   // removal was a success, make sure we cleanup properly\n                    moveObjectToTrash(optional.get());\n                    status = getCommodityDAO().removeSecurityHistory(node, optional.get());\n\n                    logInfo(ResourceUtils.getString(\"Message.RemovingSecurityHistory\", date, node.getSymbol()));\n                }\n            }\n\n            Message message;\n\n            if (status) {\n                clearCachedAccountBalance(node);\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_REMOVE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Remove a {@code SecurityHistoryEvent} from a {@code SecurityNode}.\n     *\n     * @param node         {@code SecurityNode} to remove history from\n     * @param historyEvent the {@code SecurityHistoryEvent} to remove\n     * @return {@code true} if the {@code SecurityHistoryEvent} was found and removed\n     */\n    public boolean removeSecurityHistoryEvent(@NotNull final SecurityNode node, @NotNull final SecurityHistoryEvent historyEvent) {\n        dataLock.writeLock().lock();\n\n        boolean status;\n\n        try {\n            status = node.removeSecurityHistoryEvent(historyEvent);\n\n            if (status) {   // removal was a success, make sure we cleanup properly\n                moveObjectToTrash(historyEvent);\n                status = getCommodityDAO().removeSecurityHistoryEvent(node, historyEvent);\n            }\n\n            Message message;\n\n            if (status) {\n                clearCachedAccountBalance(node);\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_EVENT_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_HISTORY_EVENT_REMOVE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    private Config getConfig() {\n\n        dataLock.readLock().lock();\n\n        try {\n            if (config == null) {\n                config = getConfigDAO().getDefaultConfig();\n            }\n            return config;\n\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public CurrencyNode getDefaultCurrency() {\n\n        dataLock.readLock().lock();\n\n        try {\n            CurrencyNode node = getConfig().getDefaultCurrency();\n\n            if (node == null) {\n                logger.warning(\"No default currency assigned\");\n            }\n\n            return node;\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public void setDefaultCurrency(final CurrencyNode defaultCurrency) {\n\n        // make sure the new default is persisted if it has not been\n        if (!isStored(defaultCurrency)) {\n            addCurrency(defaultCurrency);\n        }\n\n        dataLock.writeLock().lock();\n\n        try {\n            final Config currencyConfig = getConfig();\n            currencyConfig.setDefaultCurrency(defaultCurrency);\n            getConfigDAO().update(currencyConfig);\n\n            logInfo(\"Setting default currency: \" + defaultCurrency);\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, currencyConfig);\n            messageBus.fireEvent(message);\n\n            Account root = getRootAccount();\n\n            // The root account holds a reference to the default currency\n            root.setCurrencyNode(defaultCurrency);\n            getAccountDAO().updateAccount(root);\n\n            message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY, this);\n            message.setObject(MessageProperty.ACCOUNT, root);\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public void setExchangeRate(final CurrencyNode baseCurrency, final CurrencyNode exchangeCurrency,\n                                final BigDecimal rate) {\n        setExchangeRate(baseCurrency, exchangeCurrency, rate, LocalDate.now());\n    }\n\n    public void setExchangeRate(final CurrencyNode baseCurrency, final CurrencyNode exchangeCurrency,\n                                final BigDecimal rate, final LocalDate localDate) {\n        Objects.requireNonNull(rate);\n\n        if (rate.compareTo(BigDecimal.ZERO) < 1) {\n            throw new EngineException(\"Rate must be greater than zero\");\n        }\n\n        if (baseCurrency.equals(exchangeCurrency)) {\n            return;\n        }\n\n        // find the correct ExchangeRate and create if needed\n        ExchangeRate exchangeRate = getExchangeRate(baseCurrency, exchangeCurrency);\n\n        if (exchangeRate == null) {\n            exchangeRate = new ExchangeRate(buildExchangeRateId(baseCurrency, exchangeCurrency));\n            getCommodityDAO().addExchangeRate(exchangeRate);\n        }\n\n        // Remove old history of the same date if it exists\n        if (exchangeRate.contains(localDate)) {\n            removeExchangeRateHistory(exchangeRate, exchangeRate.getHistory(localDate));\n        }\n\n        dataLock.writeLock().lock();\n\n        try {\n            // create the new history node\n            ExchangeRateHistoryNode historyNode;\n\n            if (baseCurrency.getSymbol().compareToIgnoreCase(exchangeCurrency.getSymbol()) > 0) {\n                historyNode = new ExchangeRateHistoryNode(localDate, rate);\n            } else {\n                historyNode = new ExchangeRateHistoryNode(localDate, BigDecimal.ONE.divide(rate, MathConstants.mathContext));\n            }\n\n            final Message message;\n\n            boolean result = false;\n\n            if (exchangeRate.addHistoryNode(historyNode)) {\n                result = getCommodityDAO().addExchangeRateHistory(exchangeRate);\n            }\n\n            if (result) {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.EXCHANGE_RATE_ADD, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.EXCHANGE_RATE_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.EXCHANGE_RATE, exchangeRate);\n\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public void removeExchangeRateHistory(final ExchangeRate exchangeRate, final ExchangeRateHistoryNode history) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            final Message message;\n\n            boolean result = false;\n\n            if (exchangeRate.contains(history)) {\n                if (exchangeRate.removeHistoryNode(history)) {\n                    moveObjectToTrash(history);\n                    result = getCommodityDAO().removeExchangeRateHistory(exchangeRate);\n                }\n            }\n\n            if (result) {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.EXCHANGE_RATE_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.COMMODITY, ChannelEvent.EXCHANGE_RATE_REMOVE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.EXCHANGE_RATE, exchangeRate);\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Modifies an existing currency node in place. The supplied node should not be a reference to the original\n     *\n     * @param oldNode      old CommodityNode\n     * @param templateNode template CommodityNode\n     * @return true if successful\n     */\n    public boolean updateCommodity(final CommodityNode oldNode, final CommodityNode templateNode) {\n        Objects.requireNonNull(oldNode);\n        Objects.requireNonNull(templateNode);\n\n        if (oldNode == templateNode) {\n            throw new EngineException(\"node were the same\");\n        }\n\n        dataLock.writeLock().lock();\n\n        try {\n            boolean status;\n\n            if (oldNode.getClass().equals(templateNode.getClass())) {\n\n                oldNode.setDescription(templateNode.getDescription());\n                oldNode.setPrefix(templateNode.getPrefix());\n                oldNode.setScale(templateNode.getScale());\n                oldNode.setSuffix(templateNode.getSuffix());\n\n                if (templateNode instanceof SecurityNode) {\n                    oldNode.setSymbol(templateNode.getSymbol()); // allow symbol to change\n\n                    ((SecurityNode) oldNode).setReportedCurrencyNode(((SecurityNode) templateNode).getReportedCurrencyNode());\n\n                    ((SecurityNode) oldNode).setQuoteSource(((SecurityNode) templateNode).getQuoteSource());\n\n                    ((SecurityNode) oldNode).setISIN(((SecurityNode) templateNode).getISIN());\n                }\n\n                status = getCommodityDAO().updateCommodityNode(oldNode);\n            } else {\n                status = false;\n                logger.warning(\"Template object class did not match old object class\");\n            }\n\n            final Message message;\n\n            if (templateNode instanceof SecurityNode) {\n                if (status) {\n                    message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_MODIFY, this);\n                    message.setObject(MessageProperty.COMMODITY, oldNode);\n                } else {\n                    message = new Message(MessageChannel.COMMODITY, ChannelEvent.SECURITY_MODIFY_FAILED, this);\n                    message.setObject(MessageProperty.COMMODITY, templateNode);\n                }\n            } else {\n                if (status) {\n                    message = new Message(MessageChannel.COMMODITY, ChannelEvent.CURRENCY_MODIFY, this);\n                    message.setObject(MessageProperty.COMMODITY, oldNode);\n                } else {\n                    message = new Message(MessageChannel.COMMODITY, ChannelEvent.CURRENCY_MODIFY_FAILED, this);\n                    message.setObject(MessageProperty.COMMODITY, templateNode);\n                }\n            }\n\n            messageBus.fireEvent(message);\n            return status;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    private boolean updateReminder(final Reminder reminder) {\n        final boolean result = getReminderDAO().updateReminder(reminder);\n\n        final Message message;\n\n        if (result) {\n            message = new Message(MessageChannel.REMINDER, ChannelEvent.REMINDER_UPDATE, this);\n        } else {\n            message = new Message(MessageChannel.REMINDER, ChannelEvent.REMINDER_UPDATE_FAILED, this);\n        }\n\n        message.setObject(MessageProperty.REMINDER, reminder);\n\n        messageBus.fireEvent(message);\n\n        return result;\n    }\n\n    public String getAccountSeparator() {\n\n        dataLock.readLock().lock();\n\n        try {\n            if (accountSeparator == null) {\n                accountSeparator = getConfig().getAccountSeparator();\n            }\n            return accountSeparator;\n\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public void setAccountSeparator(final String separator) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            accountSeparator = separator;\n            Config localConfig = getConfig();\n\n            localConfig.setAccountSeparator(separator);\n\n            getConfigDAO().update(localConfig);\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, localConfig);\n\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a list of all Accounts excluding the rootAccount.\n     *\n     * @return List of accounts\n     */\n    public List<Account> getAccountList() {\n        final List<Account> accounts = getAccountDAO().getAccountList();\n        accounts.remove(getRootAccount());\n\n        return accounts;\n    }\n\n    public Account getAccountByUuid(final UUID id) {\n        return getAccountDAO().getAccountByUuid(id);\n    }\n\n    /**\n     * Search for an account with a matching account name.\n     *\n     * @param accountName Account name to search for. <b>Must not be null</b>\n     * @return The matching account. {@code null} if not found.\n     */\n    public Account getAccountByName(@NotNull final String accountName) {\n        Objects.requireNonNull(accountName);\n\n        final List<Account> list = getAccountList();\n\n        // sort for consistent search order\n        Collections.sort(list);\n\n        for (final Account account : list) {\n            if (accountName.equals(account.getName())) {\n                return account;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Returns a list of IncomeAccounts excluding the rootIncomeAccount.\n     *\n     * @return List of income accounts\n     */\n    @NotNull\n    public List<Account> getIncomeAccountList() {\n        return getAccountDAO().getIncomeAccountList();\n    }\n\n    /**\n     * Returns a list of ExpenseAccounts excluding the rootExpenseAccount.\n     *\n     * @return List if expense accounts\n     */\n    @NotNull\n    public List<Account> getExpenseAccountList() {\n        return getAccountDAO().getExpenseAccountList();\n    }\n\n    /**\n     * Returns a list of all accounts excluding the rootAccount and IncomeAccounts and ExpenseAccounts.\n     *\n     * @return List of investment accounts\n     */\n    public List<Account> getInvestmentAccountList() {\n\n        return getAccountDAO().getInvestmentAccountList();\n    }\n\n    public void refresh(final StoredObject object) {\n        eDAO.refresh(object);\n    }\n\n    /**\n     * Adds a new account.\n     *\n     * @param parent The parent account\n     * @param child  A new Account object\n     * @return true if successful\n     */\n    public boolean addAccount(final Account parent, final Account child) {\n        Objects.requireNonNull(child);\n        Objects.requireNonNull(child.getUuid());\n\n        if (child.getAccountType() == AccountType.ROOT) {\n            throw new IllegalArgumentException(\"Invalid Account\");\n        }\n\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n            boolean result;\n\n            result = parent.addChild(child);\n\n            if (result) {\n                result = getAccountDAO().addAccount(parent, child);\n            }\n\n            if (result) {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_ADD, this);\n                message.setObject(MessageProperty.ACCOUNT, child);\n                messageBus.fireEvent(message);\n\n                logInfo(rb.getString(\"Message.AccountAdd\"));\n                result = true;\n            } else {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_ADD_FAILED, this);\n                message.setObject(MessageProperty.ACCOUNT, child);\n                messageBus.fireEvent(message);\n                result = false;\n            }\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Return the root account.\n     *\n     * @return RootAccount\n     */\n    public RootAccount getRootAccount() {\n\n        dataLock.readLock().lock();\n\n        try {\n            if (rootAccount == null) {\n                rootAccount = getAccountDAO().getRootAccount();\n            }\n            return rootAccount;\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Move an account to a new parent account..\n     *\n     * @param account   account to move\n     * @param newParent the new parent account\n     * @return true if successful\n     */\n    public boolean moveAccount(final Account account, final Account newParent) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(newParent);\n\n        dataLock.writeLock().lock();\n\n        try {\n            // cannot invert the child/parent relationship of an account\n            if (account.contains(newParent)) {\n                Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY_FAILED, this);\n                message.setObject(MessageProperty.ACCOUNT, account);\n                messageBus.fireEvent(message);\n\n                logInfo(rb.getString(\"Message.AccountMoveFailed\"));\n\n                return false;\n            }\n\n            Account oldParent = account.getParent();\n\n            if (oldParent != null) { // check for detached account\n\n                oldParent.removeChild(account);\n\n                getAccountDAO().updateAccount(account);\n                getAccountDAO().updateAccount(oldParent);\n\n                Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY, this);\n                message.setObject(MessageProperty.ACCOUNT, oldParent);\n\n                messageBus.fireEvent(message);\n            }\n\n            newParent.addChild(account);\n\n            getAccountDAO().updateAccount(account);\n            getAccountDAO().updateAccount(newParent);\n\n            Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY, this);\n            message.setObject(MessageProperty.ACCOUNT, newParent);\n\n            messageBus.fireEvent(message);\n\n            logInfo(rb.getString(MESSAGE_ACCOUNT_MODIFY));\n\n            return true;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Changes an Account's code.\n     *\n     * @param account Account to change\n     * @param code    new code\n     * @return true is successful\n     */\n    public boolean setAccountCode(final Account account, final int code) {\n        account.setAccountCode(code);\n\n        boolean result = getAccountDAO().updateAccount(account);\n\n        if (result) {\n            final Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY, this);\n            message.setObject(MessageProperty.ACCOUNT, account);\n            messageBus.fireEvent(message);\n\n            logInfo(rb.getString(MESSAGE_ACCOUNT_MODIFY));\n        } else {\n            final Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY_FAILED, this);\n            message.setObject(MessageProperty.ACCOUNT, account);\n            messageBus.fireEvent(message);\n        }\n\n        return result;\n    }\n\n    /**\n     * Modifies an existing account given an account as a template. The type of the account cannot be changed.\n     *\n     * @param template The Account object to use as a template\n     * @param account  The existing account\n     * @return true if successful\n     */\n    public boolean modifyAccount(final Account template, final Account account) {\n\n        boolean result;\n        Message message;\n\n        dataLock.writeLock().lock();\n\n        try {\n            account.setName(template.getName());\n            account.setDescription(template.getDescription());\n            account.setNotes(template.getNotes());\n\n            account.setLocked(template.isLocked());\n            account.setPlaceHolder(template.isPlaceHolder());\n            account.setVisible(template.isVisible());\n            account.setExcludedFromBudget(template.isExcludedFromBudget());\n            account.setAccountNumber(template.getAccountNumber());\n            account.setBankId(template.getBankId());\n            account.setAccountCode(template.getAccountCode());\n\n            if (account.getAccountType().isMutable()) {\n                account.setAccountType(template.getAccountType());\n            }\n\n            // allow allow a change if the account does not contain transactions\n            if (account.getTransactionCount() == 0) {\n                account.setCurrencyNode(template.getCurrencyNode());\n            }\n\n            result = getAccountDAO().updateAccount(account);\n\n            if (result) {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY, this);\n                message.setObject(MessageProperty.ACCOUNT, account);\n                messageBus.fireEvent(message);\n\n                logInfo(rb.getString(MESSAGE_ACCOUNT_MODIFY));\n            } else {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY_FAILED, this);\n                message.setObject(MessageProperty.ACCOUNT, account);\n                messageBus.fireEvent(message);\n            }\n\n            /* Check to see if the account needs to be moved */\n            if (account.parentAccount != template.parentAccount && template.parentAccount != null && result) {\n                if (!moveAccount(account, template.parentAccount)) {\n                    logWarning(rb.getString(\"Message.Error.MoveAccount\"));\n                    result = false;\n                }\n            }\n\n            // Force clearing of any budget goals if an empty account has been changed to become a place holder\n            if (account.isPlaceHolder()) {\n                purgeBudgetGoal(account);\n            }\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Purges any {@code BudgetGoal} associated with an account.\n     *\n     * @param account {@code Account} to remove all associated budget goal history\n     */\n    private void purgeBudgetGoal(@NotNull final Account account) {\n        // clear budget history\n        for (final Budget budget : getBudgetList()) {\n            budget.removeBudgetGoal(account);\n            if (!updateBudget(budget)) {\n                logWarning(\"Unable to remove account goals from the budget\");\n            }\n        }\n    }\n\n    /**\n     * Sets the account number of an account.\n     *\n     * @param account account to change\n     * @param number  new account number\n     */\n    public void setAccountNumber(final Account account, final String number) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            account.setAccountNumber(number);\n            getAccountDAO().updateAccount(account);\n\n            Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY, this);\n            message.setObject(MessageProperty.ACCOUNT, account);\n            messageBus.fireEvent(message);\n\n            logInfo(rb.getString(MESSAGE_ACCOUNT_MODIFY));\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Sets an attribute for an {@code Account}.  The key and values are string based\n     *\n     * @param account {@code Account} to add or update an attribute\n     * @param key     the key for the attribute\n     * @param value   the value of the attribute\n     */\n    public void setAccountAttribute(final Account account, @NotNull final String key, @Nullable final String value) {\n        // Throw an error if the value exceeds the maximum length\n        if (value != null && value.length() > Account.MAX_ATTRIBUTE_LENGTH) {\n            Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_MODIFY_FAILED, this);\n            message.setObject(MessageProperty.ACCOUNT, account);\n            messageBus.fireEvent(message);\n\n            logInfo(\"The maximum length of the attribute was exceeded\");\n\n            return;\n        }\n\n        dataLock.writeLock().lock();\n\n        try {\n            account.setAttribute(key, value);\n            getAccountDAO().updateAccount(account);\n\n            Message message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_ATTRIBUTE_MODIFY, this);\n            message.setObject(MessageProperty.ACCOUNT, account);\n            messageBus.fireEvent(message);\n\n            logInfo(rb.getString(MESSAGE_ACCOUNT_MODIFY));\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Returns an {@code Account} attribute.\n     *\n     * @param account account to extract attribute from\n     * @param key     the attribute key\n     * @return the attribute if found\n     * @see #setAccountAttribute\n     */\n    public static String getAccountAttribute(@NotNull final Account account, @NotNull final String key) {\n        return account.getAttribute(key);\n    }\n\n    /**\n     * Removes an existing account given it's ID.\n     *\n     * @param account The account to remove\n     * @return true if successful\n     */\n    public boolean removeAccount(final Account account) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            boolean result = false;\n\n            if (account.getTransactionCount() == 0 && account.getChildCount() == 0) {\n                Account parent = account.getParent();\n\n                if (parent != null) {\n                    result = parent.removeChild(account);\n\n                    if (result) {\n                        getAccountDAO().updateAccount(parent);\n\n                        // clear budget history\n                        purgeBudgetGoal(account);\n                    }\n                }\n\n                moveObjectToTrash(account);\n            }\n\n            Message message;\n\n            if (result) {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_REMOVE, this);\n                message.setObject(MessageProperty.ACCOUNT, account);\n                messageBus.fireEvent(message);\n\n                logInfo(rb.getString(\"Message.AccountRemove\"));\n            } else {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_REMOVE_FAILED, this);\n                message.setObject(MessageProperty.ACCOUNT, account);\n                messageBus.fireEvent(message);\n            }\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public Future<Path> getAttachment(final String attachment) {\n        return attachmentManager.getAttachment(attachment);\n    }\n\n    public boolean addAttachment(final Path path, final boolean copy) {\n        boolean result = false;\n\n        try {\n            result = attachmentManager.addAttachment(path, copy);\n        } catch (IOException e) {\n            logSevere(e.getLocalizedMessage());\n        }\n\n        return result;\n    }\n\n    public boolean removeAttachment(final String attachment) {\n        return attachmentManager.removeAttachment(attachment);\n    }\n\n    /**\n     * Sets the amortize object of an account.\n     *\n     * @param account        The Liability account to change\n     * @param amortizeObject the new AmortizeObject\n     * @return true if successful\n     */\n    public boolean setAmortizeObject(final Account account, final AmortizeObject amortizeObject) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            if (account != null && amortizeObject != null && account.getAccountType() == AccountType.LIABILITY) {\n\n                account.setAmortizeObject(amortizeObject);\n\n                if (!getAccountDAO().updateAccount(account)) {\n                    logSevere(\"Was not able to save the amortize object\");\n                }\n\n                return true;\n            }\n            return false;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Toggles the visibility of an account given its ID.\n     *\n     * @param account The account to toggle visibility\n     */\n    public void toggleAccountVisibility(final Account account) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n\n            account.setVisible(!account.isVisible());\n\n            if (getAccountDAO().toggleAccountVisibility(account)) {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_VISIBILITY_CHANGE, this);\n            } else {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_VISIBILITY_CHANGE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.ACCOUNT, account);\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Adds a SecurityNode from a InvestmentAccount.\n     *\n     * @param account destination account\n     * @param node    SecurityNode to add\n     * @return true if add was successful\n     */\n    public boolean addAccountSecurity(final Account account, final SecurityNode node) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n\n            boolean result = account.addSecurity(node);\n\n            if (result) {\n                result = getAccountDAO().addAccountSecurity(account, node);\n            }\n\n            if (result) {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_SECURITY_ADD, this);\n            } else {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_SECURITY_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.ACCOUNT, account);\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return result;\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Removes a SecurityNode from an InvestmentAccount.\n     *\n     * @param account Account to remove SecurityNode from\n     * @param node    SecurityNode to remove\n     * @return true if successful\n     */\n    private boolean removeAccountSecurity(final Account account, final SecurityNode node) {\n        Objects.requireNonNull(node);\n\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n            boolean result = account.removeSecurity(node);\n\n            if (result) {\n                getAccountDAO().updateAccount(account);\n            }\n\n            if (result) {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_SECURITY_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.ACCOUNT, ChannelEvent.ACCOUNT_SECURITY_REMOVE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.ACCOUNT, account);\n            message.setObject(MessageProperty.COMMODITY, node);\n            messageBus.fireEvent(message);\n\n            return result;\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Update an account's securities list. This compares the old list of securities and the supplied list and adds or\n     * removes securities to make sure the lists are the same.\n     *\n     * @param acc  Destination account\n     * @param list Collection of SecurityNodes\n     * @return true if successful\n     */\n    public boolean updateAccountSecurities(final Account acc, final Collection<SecurityNode> list) {\n\n        boolean result = true;\n\n        if (acc.memberOf(AccountGroup.INVEST)) {\n            dataLock.writeLock().lock();\n\n            try {\n                final Collection<SecurityNode> oldList = acc.getSecurities();\n\n                for (SecurityNode node : oldList) {\n                    if (!list.contains(node)) {\n                        if (!removeAccountSecurity(acc, node)) {\n                            logWarning(ResourceUtils.getString(\"Message.Error.SecurityAccountRemove\", node.toString(),\n                                    acc.getName()));\n                            result = false;\n                        }\n                    }\n                }\n\n                for (SecurityNode node : list) {\n                    if (!oldList.contains(node)) {\n                        if (!addAccountSecurity(acc, node)) {\n                            logWarning(ResourceUtils.getString(\"Message.Error.SecurityAccountRemove\", node.toString(),\n                                    acc.getName()));\n                            result = false;\n                        }\n                    }\n                }\n            } finally {\n                dataLock.writeLock().unlock();\n            }\n        }\n\n        return result;\n    }\n\n    public boolean addBudget(final Budget budget) {\n\n        boolean result;\n\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n\n            result = getBudgetDAO().add(budget);\n\n            if (result) {\n                message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_ADD, this);\n            } else {\n                message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_ADD_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.BUDGET, budget);\n            messageBus.fireEvent(message);\n\n            return result;\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public boolean removeBudget(final Budget budget) {\n\n        boolean result = false;\n\n        dataLock.writeLock().lock();\n\n        try {\n            moveObjectToTrash(budget);\n\n            Message message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_REMOVE, this);\n\n            message.setObject(MessageProperty.BUDGET, budget);\n            messageBus.fireEvent(message);\n\n            result = true;\n        } catch (final Exception ex) {\n            logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n\n        return result;\n    }\n\n    public void updateBudgetGoals(final Budget budget, final Account account, final BudgetGoal newGoals) {\n        dataLock.writeLock().lock();\n\n        try {\n            BudgetGoal oldGoals = budget.getBudgetGoal(account);\n\n            budget.setBudgetGoal(account, newGoals);\n\n            moveObjectToTrash(oldGoals);    // need to keep the old goal around, will be cleaned up later, orphan removal causes refresh issues\n\n            updateBudgetGoals(budget, account);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    private void updateBudgetGoals(final Budget budget, final Account account) {\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n\n            boolean result = getBudgetDAO().update(budget);\n\n            if (result) {\n                message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_GOAL_UPDATE, this);\n            } else {\n                message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_GOAL_UPDATE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.BUDGET, budget);\n            message.setObject(MessageProperty.ACCOUNT, account);\n\n            messageBus.fireEvent(message);\n\n            logger.log(Level.FINE, \"Budget goal updated for {0}\", account.getPathName());\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public boolean updateBudget(final Budget budget) {\n\n        boolean result;\n\n        dataLock.writeLock().lock();\n\n        try {\n            Message message;\n\n            result = getBudgetDAO().update(budget);\n\n            if (result) {\n                message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_UPDATE, this);\n            } else {\n                message = new Message(MessageChannel.BUDGET, ChannelEvent.BUDGET_UPDATE_FAILED, this);\n            }\n\n            message.setObject(MessageProperty.BUDGET, budget);\n            messageBus.fireEvent(message);\n\n            logger.log(Level.FINE, \"Budget updated\");\n\n            return result;\n\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public List<Budget> getBudgetList() {\n\n        dataLock.readLock().lock();\n\n        try {\n            return getBudgetDAO().getBudgets();\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public Budget getBudgetByUuid(final UUID uuid) {\n        return getBudgetDAO().getBudgetByUuid(uuid);\n    }\n\n    public boolean isTransactionValid(final Transaction transaction) {\n\n        for (final Account a : transaction.getAccounts()) {\n            if (a.isLocked()) {\n                logWarning(rb.getString(\"Message.TransactionAccountLocked\"));\n                return false;\n            }\n        }\n\n        if (transaction.isMarkedForRemoval()) {\n            logger.log(Level.SEVERE, \"Transaction already marked for removal\", new Throwable());\n            return false;\n        }\n\n        if (eDAO.getObjectByUuid(Transaction.class, transaction.getUuid()) != null) {\n            logger.log(Level.WARNING, \"Transaction UUID was not unique\");\n            return false;\n        }\n\n        if (transaction.size() < 1) {\n            logger.log(Level.WARNING, \"Invalid Transaction\");\n            return false;\n        }\n\n        for (final TransactionEntry e : transaction.getTransactionEntries()) {\n            if (e == null) {\n                logger.log(Level.WARNING, \"Null TransactionEntry\");\n                return false;\n            }\n        }\n\n        for (final TransactionEntry e : transaction.getTransactionEntries()) {\n            if (e.getTransactionTag() == null) {\n                logger.log(Level.WARNING, \"Null TransactionTag\");\n                return false;\n            }\n        }\n\n        for (final TransactionEntry e : transaction.getTransactionEntries()) {\n            if (e.getCreditAccount() == null) {\n                logger.log(Level.WARNING, \"Null Credit Account\");\n                return false;\n            }\n\n            if (e.getDebitAccount() == null) {\n                logger.log(Level.WARNING, \"Null Debit Account\");\n                return false;\n            }\n\n            if (e.getCreditAmount() == null) {\n                logger.log(Level.WARNING, \"Null Credit Amount\");\n                return false;\n            }\n\n            if (e.getDebitAmount() == null) {\n                logger.log(Level.WARNING, \"Null Debit Amount\");\n                return false;\n            }\n        }\n\n        if (transaction.getTransactionType() == TransactionType.SPLITENTRY && transaction.getCommonAccount() == null) {\n            logger.log(Level.WARNING, \"Entries do not share a common account\");\n            return false;\n        }\n\n        if (transaction instanceof InvestmentTransaction) {\n            final InvestmentTransaction investmentTransaction = (InvestmentTransaction) transaction;\n\n            if (!investmentTransaction.getInvestmentAccount().containsSecurity(investmentTransaction.getSecurityNode())) {\n                logger.log(Level.WARNING, \"Investment Account is missing the security\");\n                return false;\n            }\n        }\n\n        return transaction.getTransactionType() != TransactionType.INVALID;\n    }\n\n    /**\n     * Determine if a StoredObject is persisted in the database.\n     *\n     * @param object StoredObject to check\n     * @return true if persisted\n     */\n    public boolean isStored(final StoredObject object) {\n        return eDAO.getObjectByUuid(StoredObject.class, object.getUuid()) != null;\n    }\n\n    public boolean addTransaction(final Transaction transaction) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            boolean result = isTransactionValid(transaction);\n\n            if (result) {\n                /* Add the transaction to each account */\n                transaction.getAccounts().stream()\n                        .filter(account -> !account.addTransaction(transaction))\n                        .forEach(account -> logSevere(\"Failed to add the Transaction\"));\n                result = getTransactionDAO().addTransaction(transaction);\n\n                logInfo(rb.getString(\"Message.TransactionAdd\"));\n\n                /* If successful, extract and enter a default exchange rate for the transaction date if a rate has not been set */\n                if (result) {\n                    // no rate for the date has been set\n                    transaction.getTransactionEntries().stream()\n                            .filter(TransactionEntry::isMultiCurrency)\n                            .forEach(entry -> {\n                                final ExchangeRate rate = getExchangeRate(entry.getDebitAccount().getCurrencyNode(),\n                                        entry.getCreditAccount().getCurrencyNode());\n\n                                if (rate.getRate(transaction.getLocalDate()).compareTo(BigDecimal.ZERO) == 0) { // no rate for the date has been set\n                                    final BigDecimal exchangeRate = entry.getDebitAmount().abs()\n                                                                            .divide(entry.getCreditAmount().abs(),\n                                                                                    MathConstants.mathContext);\n\n                                    setExchangeRate(entry.getCreditAccount().getCurrencyNode(),\n                                            entry.getDebitAccount().getCurrencyNode(), exchangeRate, transaction.getLocalDate());\n                                }\n                            });\n                }\n            }\n\n            postTransactionAdd(transaction, result);\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public boolean removeTransaction(final Transaction transaction) {\n\n        dataLock.writeLock().lock();\n\n        try {\n            for (final Account account : transaction.getAccounts()) {\n                if (account.isLocked()) {\n                    logWarning(rb.getString(\"Message.TransactionRemoveLocked\"));\n                    return false;\n                }\n            }\n\n            /* Remove the transaction from each account */\n            transaction.getAccounts().stream()\n                    .filter(account -> !account.removeTransaction(transaction))\n                    .forEach(account -> logSevere(\"Failed to remove the Transaction\"));\n\n            logInfo(rb.getString(\"Message.TransactionRemove\"));\n\n            boolean result = getTransactionDAO().removeTransaction(transaction);\n\n            // move transactions into the trash\n            if (result) {\n                moveObjectToTrash(transaction);\n            }\n\n            postTransactionRemove(transaction, result);\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Changes the reconciled state of a transaction.\n     *\n     * @param transaction transaction to change\n     * @param account     account to change state for\n     * @param state       new reconciled state\n     */\n    public void setTransactionReconciled(final Transaction transaction, final Account account, final ReconciledState state) {\n        dataLock.writeLock().lock(); // hold a write lock to ensure nothing slips in between the remove and add\n\n        try {\n            final Transaction newTransaction = (Transaction) transaction.clone();\n\n            ReconcileManager.reconcileTransaction(account, newTransaction, state);\n\n            if (removeTransaction(transaction)) {\n                addTransaction(newTransaction);\n            }\n        } catch (final CloneNotSupportedException e) {\n            logger.log(Level.SEVERE, \"Failed to reconcile the Transaction\", e);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public List<String> getTransactionNumberList() {\n        dataLock.readLock().lock();\n\n        try {\n            return getConfig().getTransactionNumberList();\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public void setTransactionNumberList(final List<String> list) {\n        dataLock.writeLock().lock();\n\n        try {\n            final Config transactionConfig = getConfig();\n\n            transactionConfig.setTransactionNumberList(list);\n            getConfigDAO().update(transactionConfig);\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, transactionConfig);\n\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Get all transactions.\n     *\n     * @return List of transactions that may be altered without concern of side effects\n     */\n    public List<Transaction> getTransactions() {\n        return getTransactionDAO().getTransactions();\n    }\n\n    /**\n     * Returns a list of transactions with external links.\n     *\n     * @return List of transactions that may be altered without concern of side effects\n     */\n    public List<Transaction> getTransactionsWithAttachments() {\n        return getTransactionDAO().getTransactionsWithAttachments();\n    }\n\n    public Transaction getTransactionByUuid(final UUID uuid) {\n        return getTransactionDAO().getTransactionByUuid(uuid);\n    }\n\n    private void postTransactionAdd(final Transaction transaction, final boolean result) {\n\n        for (Account a : transaction.getAccounts()) {\n            Message message;\n\n            if (result) {\n                message = new Message(MessageChannel.TRANSACTION, ChannelEvent.TRANSACTION_ADD, this);\n            } else {\n                message = new Message(MessageChannel.TRANSACTION, ChannelEvent.TRANSACTION_ADD_FAILED, this);\n            }\n            message.setObject(MessageProperty.ACCOUNT, a);\n            message.setObject(MessageProperty.TRANSACTION, transaction);\n\n            messageBus.fireEvent(message);\n        }\n    }\n\n    private void postTransactionRemove(final Transaction transaction, final boolean result) {\n\n        for (Account a : transaction.getAccounts()) {\n            Message message;\n\n            if (result) {\n                message = new Message(MessageChannel.TRANSACTION, ChannelEvent.TRANSACTION_REMOVE, this);\n            } else {\n                message = new Message(MessageChannel.TRANSACTION, ChannelEvent.TRANSACTION_REMOVE_FAILED, this);\n            }\n            message.setObject(MessageProperty.ACCOUNT, a);\n            message.setObject(MessageProperty.TRANSACTION, transaction);\n\n            messageBus.fireEvent(message);\n        }\n    }\n\n    /**\n     * Adds a new Tag\n     *\n     * @param tag Tag to add\n     * @return true is successful\n     */\n    public boolean addTag(@NotNull Tag tag) {\n        Objects.requireNonNull(tag);\n\n        dataLock.writeLock().lock();\n\n        try {\n            boolean result = eDAO.getTagDAO().add(tag);\n\n            final Message message = new Message(MessageChannel.TAG, result ? ChannelEvent.TAG_ADD\n                                                                            : ChannelEvent.TAG_ADD_FAILED, this);\n            message.setObject(MessageProperty.TAG, tag);\n            messageBus.fireEvent(message);\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Updates an existing Tag\n     *\n     * @param tag Tag to update\n     * @return true is successful\n     */\n    public boolean updateTag(@NotNull Tag tag) {\n        Objects.requireNonNull(tag);\n\n        dataLock.writeLock().lock();\n\n        try {\n            boolean result = eDAO.getTagDAO().update(tag);\n\n            final Message message = new Message(MessageChannel.TAG, result ? ChannelEvent.TAG_MODIFY\n                                                                            : ChannelEvent.TAG_MODIFY_FAILED, this);\n            message.setObject(MessageProperty.TAG, tag);\n            messageBus.fireEvent(message);\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Retrieves all Tags\n     *\n     * @return a Set of Tags.\n     */\n    @NotNull\n    public Set<Tag> getTags() {\n        dataLock.readLock().lock();\n\n        try {\n            return eDAO.getTagDAO().getTags();\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Retrieves the Tags that are in use\n     *\n     * @return a Set of Tags.\n     */\n    @NotNull\n    public Set<Tag> getTagsInUse() {\n        dataLock.readLock().lock();\n\n        try {\n            return getTransactions()\n                           .parallelStream()\n                           .flatMap((Function<Transaction, Stream<Tag>>) transaction -> transaction.getTags().stream())\n                           .collect(Collectors.toSet());\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Removes a Tag from the database.\n     * <p>\n     * The removal will fail if the Tag is in use.\n     *\n     * @param tag Tag to remove\n     * @return true if successful\n     */\n    public boolean removeTag(@NotNull Tag tag) {\n        Objects.requireNonNull(tag);\n\n        dataLock.writeLock().lock();\n\n        try {\n            boolean result = !getTagsInUse().contains(tag);  // make sure the tag is not used\n\n            if (result) {\n                result = moveObjectToTrash(tag);\n            }\n\n            final Message message = new Message(MessageChannel.TAG, result ? ChannelEvent.TAG_REMOVE\n                                                                            : ChannelEvent.TAG_REMOVE_FAILED, this);\n            message.setObject(MessageProperty.TAG, tag);\n            messageBus.fireEvent(message);\n\n            return result;\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the unique identifier for this engine instance.\n     *\n     * @return uuid\n     */\n    public String getUuid() {\n        return uuid;\n    }\n\n    public void setPreference(@NotNull final String key, @Nullable final String value) {\n        dataLock.writeLock().lock();\n\n        try {\n            getConfig().setPreference(key, value);\n            getConfigDAO().update(getConfig());\n\n            config = null;  // clear stale cached reference\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, getConfig());\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    @Nullable\n    public String getPreference(@NotNull final String key) {\n        dataLock.readLock().lock();\n\n        try {\n            return getConfig().getPreference(key);\n        } finally {\n            dataLock.readLock().unlock();\n        }\n    }\n\n    public boolean getBoolean(@NotNull final String key, final boolean defaultValue) {\n        boolean value = defaultValue;\n\n        final String stringResult = getPreference(key);\n\n        if (stringResult != null && !stringResult.isEmpty()) {\n            value = Boolean.parseBoolean(stringResult);\n        }\n\n        return value;\n    }\n\n    public void putBoolean(@NotNull final String key, final boolean value) {\n        setPreference(key, Boolean.toString(value));\n    }\n\n    public boolean createBackups() {\n        return getConfig().createBackups();\n    }\n\n    public void setCreateBackups(final boolean createBackups) {\n        dataLock.writeLock().lock();\n\n        try {\n            final Config backupConfig = getConfig();\n\n            backupConfig.setCreateBackups(createBackups);\n            getConfigDAO().update(backupConfig);\n\n            config = null;  // clear stale cached reference\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, backupConfig);\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public int getRetainedBackupLimit() {\n        return getConfig().getRetainedBackupLimit();\n    }\n\n    public void setRetainedBackupLimit(final int retainedBackupLimit) {\n        dataLock.writeLock().lock();\n\n        try {\n            final Config backupConfig = getConfig();\n\n            backupConfig.setRetainedBackupLimit(retainedBackupLimit);\n            getConfigDAO().update(backupConfig);\n\n            config = null;  // clear stale cached reference\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, backupConfig);\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    public boolean removeOldBackups() {\n        return getConfig().removeOldBackups();\n    }\n\n    public void setRemoveOldBackups(final boolean removeOldBackups) {\n        dataLock.writeLock().lock();\n\n        try {\n            final Config backupConfig = getConfig();\n\n            backupConfig.setRemoveOldBackups(removeOldBackups);\n            getConfigDAO().update(backupConfig);\n\n            config = null;  // clear stale cached reference\n\n            Message message = new Message(MessageChannel.CONFIG, ChannelEvent.CONFIG_MODIFY, this);\n            message.setObject(MessageProperty.CONFIG, backupConfig);\n            messageBus.fireEvent(message);\n        } finally {\n            dataLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Handles Background removal of {@code SecurityNode} history.  This can an expensive operation that block normal\n     * operations, so the removal is partitioned into small events to prevent stalling.\n     *\n     * @param securityNode SecurityNode being processed\n     * @param daysOfWeek   Collection of {code DayOfWeek} to remove\n     */\n    public void removeSecurityHistoryByDayOfWeek(final SecurityNode securityNode, final Collection<DayOfWeek> daysOfWeek) {\n\n        final Thread thread = new Thread(() -> {\n            long delay = 0;\n\n            for (final SecurityHistoryNode historyNode : new ArrayList<>(securityNode.getHistoryNodes())) {\n                for (final DayOfWeek dayOfWeek : daysOfWeek) {\n                    if (historyNode.getLocalDate().getDayOfWeek() == dayOfWeek) {\n\n                        backgroundExecutorService.schedule(new BackgroundCallable(() -> {\n                            removeSecurityHistory(securityNode, historyNode.getLocalDate());\n                            return true;\n                        }), delay, TimeUnit.MILLISECONDS);\n\n                        delay += 750;\n                    }\n                }\n            }\n        });\n\n        thread.setDaemon(true);\n        thread.setPriority(Thread.MIN_PRIORITY);\n        thread.start();\n    }\n\n    /**\n     * Thread to monitor background update of securities and terminate is network errors are occurring\n     */\n    private class SecuritiesUpdateRunnable extends Thread {\n\n        private final List<BackgroundCallable> backgroundCallables;\n        private final int delay;\n\n        SecuritiesUpdateRunnable(final List<BackgroundCallable> callables, final int delay) {\n            this.backgroundCallables = callables;\n            this.delay = delay;\n        }\n\n        @Override\n        public void run() {\n\n            try {\n                TimeUnit.SECONDS.sleep(delay);  // for controlled delay at startup\n            } catch (final InterruptedException ignored) {\n                return;\n            }\n\n            int errors = 0;\n\n            final CompletionService<Boolean> completionService = new ExecutorCompletionService<>(backgroundExecutorService);\n\n            // submit the callables\n            for (final BackgroundCallable backgroundCallable : backgroundCallables) {\n                try {\n                    completionService.submit(backgroundCallable);\n                } catch (final RejectedExecutionException ignored) {\n                    // ignore, race to shut down the executor was won\n                }\n            }\n\n            // poll until complete or there have been too many errors\n            while (errors < MAX_ERRORS && !Thread.currentThread().isInterrupted()) {\n                try {\n                    final Future<Boolean> future = completionService.poll(1, TimeUnit.MINUTES);\n\n                    if (future == null) {   // all done, no issues\n                        break;\n                    }\n\n                    errors += future.get() ? 0 : 1;\n\n                } catch (final InterruptedException | ExecutionException e) {\n                    errors = Integer.MAX_VALUE;\n                    logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                }\n            }\n\n            // if there are too many errors, force cancellation\n            if (errors > MAX_ERRORS || Thread.currentThread().isInterrupted()) {\n                for (final BackgroundCallable backgroundCallable : backgroundCallables) {\n                    backgroundCallable.cancel = true;   // stop all other callables\n                }\n            }\n        }\n    }\n\n    /**\n     * Decorates a Callable to indicate background engine activity is occurring.\n     */\n    private class BackgroundCallable implements Callable<Boolean> {\n\n        private final Callable<Boolean> callable;\n\n        volatile boolean cancel = false;    // may be set to true to interrupt operation\n\n        BackgroundCallable(@NotNull final Callable<Boolean> callable) {\n            this.callable = callable;\n        }\n\n        @Override\n        public Boolean call() throws Exception {\n\n            if (!cancel) {\n                if (backGroundCounter.incrementAndGet() == 1) {\n                    messageBus.fireEvent(new Message(MessageChannel.SYSTEM, ChannelEvent.BACKGROUND_PROCESS_STARTED,\n                            Engine.this));\n                }\n\n                try {\n                    return callable.call();\n                } finally {\n                    if (backGroundCounter.decrementAndGet() == 0) {\n                        messageBus.fireEvent(new Message(MessageChannel.SYSTEM, ChannelEvent.BACKGROUND_PROCESS_STOPPED,\n                                Engine.this));\n                    }\n                }\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/EngineException.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\n/**\n * Unchecked Engine Exception\n */\npublic class EngineException extends RuntimeException {\n\n    public EngineException(final String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/EngineFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.function.DoubleConsumer;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport jgnash.engine.jpa.JpaNetworkServer;\nimport jgnash.engine.jpa.SqlUtils;\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.xstream.BinaryXStreamDataStore;\nimport jgnash.engine.xstream.XMLDataStore;\nimport jgnash.resource.util.OS;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.FileMagic;\nimport jgnash.util.FileMagic.FileType;\nimport jgnash.util.FileUtils;\nimport jgnash.util.Nullable;\n\n/**\n * Factory class for obtaining an engine instance.\n * <p>\n * The filename of the database or remote server must be explicitly set before an Engine instance will be returned\n *\n * @author Craig Cavanaugh\n */\npublic class EngineFactory {\n\n    public static final char[] EMPTY_PASSWORD = new char[]{};\n\n    public static final String LOCALHOST = \"localhost\";\n\n    public static final String DEFAULT = \"default\";\n\n    public static final String REMOTE_PREFIX = \"@\";\n\n    private static final String LAST_DATABASE = \"LastDatabase\";\n\n    private static final String LAST_HOST = \"LastHost\";\n\n    private static final String LAST_PORT = \"LastPort\";\n\n    private static final String USED_PASSWORD = \"LastUsedPassword\";\n\n    private static final String LAST_REMOTE = \"LastRemote\";\n\n    /**\n     * Default directory for jGnash data. To be located in the default user\n     * directory\n     */\n    private static final String DEFAULT_DIR = \"jGnash\";\n\n    private static final Logger logger = Logger.getLogger(EngineFactory.class.getName());\n\n    private static final Map<String, Engine> engineMap = new HashMap<>();\n\n    private static final Map<String, DataStore> dataStoreMap = new HashMap<>();\n\n    private EngineFactory() {\n    }\n\n    /**\n     * Registers a {@code Handler} with the class logger.\n     * This also ensures the static logger is initialized.\n     *\n     * @param handler {@code Handler} to register\n     */\n    public static void addLogHandler(final Handler handler) {\n        logger.addHandler(handler);\n    }\n\n    public static boolean doesDatabaseExist(final String database, final DataStoreType type) {\n        if (FileUtils.fileHasExtension(database)) {\n            return Files.isReadable(Paths.get(database));\n        }\n\n        return Files.isReadable(Paths.get(database + type.getDataStore().getFileExt()));\n    }\n\n    public static boolean deleteDatabase(final String database) {\n        try {\n            return Files.deleteIfExists(Paths.get(database));\n        } catch (final IOException e) {\n            logger.warning(e.getLocalizedMessage());\n            return false;\n        }\n    }\n\n    /**\n     * Returns the engine with the given name.\n     *\n     * @param name engine name to look for\n     * @return returns {@code null} if it does not exist\n     */\n    @Nullable\n    public static synchronized Engine getEngine(final String name) {\n        return engineMap.get(name);\n    }\n\n    private static void exportCompressedXML(final String engineName) {\n        final Engine oldEngine = engineMap.get(engineName);\n        final DataStore oldDataStore = dataStoreMap.get(engineName);\n\n        exportCompressedXML(oldDataStore.getFileName(), oldEngine.getStoredObjects());\n    }\n\n    public static void exportCompressedXML(final String fileName, final Collection<StoredObject> objects) {\n        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(\"yyyyMMdd-HHmm\");\n\n        final DataStore xmlDataStore = new XMLDataStore();\n\n        Path xmlFile = Paths.get(FileUtils.stripFileExtension(fileName) + \"-\" + dateTimeFormatter.format(LocalDateTime.now())\n                + xmlDataStore.getFileExt());\n\n        // push the intermediary file to the temporary directory\n        xmlFile = Paths.get(System.getProperty(\"java.io.tmpdir\") + xmlFile.getFileSystem().getSeparator()\n                + xmlFile.getFileName().toString());\n\n        xmlDataStore.saveAs(xmlFile, objects, ignored -> { });\n\n        Path zipFile = Paths.get(FileUtils.stripFileExtension(fileName) + \"-\" + dateTimeFormatter.format(LocalDateTime.now())\n                + \".zip\");\n\n        FileUtils.compressFile(xmlFile, zipFile);\n\n        try {\n            Files.delete(xmlFile);\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            logger.log(Level.WARNING, \"Was not able to delete the temporary file: {0}\", xmlFile);\n        }\n    }\n\n    public static void removeOldCompressedXML(final String fileName, final int limit) {\n        final Path path = Paths.get(fileName);\n\n        String baseFile = FileUtils.stripFileExtension(path.toString());\n\n        // '\\' on Windows platform must be replaced with '\\\\' to prevent an exception\n        if (OS.isSystemWindows()) {\n            baseFile = baseFile.replace(\"\\\\\",\"\\\\\\\\\");\n        }\n\n        // old files use the base file name plus a '-' and a 8 digit date plus a '-' and a 4 digit time stamp\n        final List<Path> fileList = FileUtils.getDirectoryListing(path.getParent(), baseFile + \"-\\\\d{8}-\\\\d{4}.zip\");\n\n        if (fileList.size() > limit) {\n            for (int i = 0; i < fileList.size() - limit; i++) {\n                try {\n                    Files.delete(fileList.get(i));\n                } catch (final IOException e) {\n                    logger.log(Level.WARNING, \"Unable to delete the file: {0}\", fileList.get(i));\n                }\n            }\n        }\n    }\n\n    public static synchronized void closeEngine(final String engineName) {\n        Engine oldEngine = engineMap.get(engineName);\n        DataStore oldDataStore = dataStoreMap.get(engineName);\n\n        if (oldEngine != null) {\n\n            // stop and wait for all working background services to complete\n            oldEngine.stopBackgroundServices();\n\n            // Post a message so the GUI knows what is going on\n            final Message message = new Message(MessageChannel.SYSTEM, ChannelEvent.FILE_CLOSING, oldEngine);\n            MessageBus.getInstance(engineName).fireBlockingEvent(message);  // block until event has been completely processed\n\n            if (oldEngine.isFileDirty()) {  // should a backup file be created?\n                // Dump an XML backup\n                if (oldEngine.createBackups() && oldDataStore.isLocal()) {\n                    exportCompressedXML(engineName);\n                }\n\n                // Purge old backups\n                if (oldEngine.removeOldBackups() && oldDataStore.isLocal()) {\n                    removeOldCompressedXML(oldDataStore.getFileName(), oldEngine.getRetainedBackupLimit());\n                }\n            } else {\n                logger.info(\"File was not dirty\");\n            }\n\n            // Initiate a complete shutdown\n            oldEngine.shutdown();\n\n            MessageBus.getInstance(engineName).setLocal();\n\n            oldDataStore.closeEngine();\n\n            engineMap.remove(engineName);\n            dataStoreMap.remove(engineName);\n        }\n    }\n\n    /**\n     * Boots a local Engine for a preexisting file. The API determines the\n     * correct file type and uses the correct DataStoreType for engine\n     * initialization. If successful, a new\n     * {@code Engine} instance will be returned.\n     *\n     * @param fileName   filename to load\n     * @param engineName engine identifier\n     * @param password   connection password\n     * @return new {@code Engine} instance if successful, null otherwise\n     * @see Engine\n     */\n    public static synchronized Engine bootLocalEngine(final String fileName, final String engineName,\n                                                      final char[] password) {\n        final DataStoreType type = getDataStoreByType(fileName);\n\n        Engine engine = null;\n\n        if (type != null) {\n            engine = bootLocalEngine(fileName, engineName, password, type);\n        }\n\n        return engine;\n    }\n\n    /**\n     * Boots a local Engine for a file. If the file does not exist, it will be created. Otherwise it will be loaded.\n     * If successful, a new {@code Engine} instance will be returned.\n     *\n     * @param fileName   filename to load or create\n     * @param engineName engine identifier\n     * @param password   password for the file\n     * @param type       {@code DataStoreType} type to use for storage\n     * @return new {@code Engine} instance if successful\n     * @see Engine\n     * @see DataStoreType\n     */\n    public static synchronized Engine bootLocalEngine(final String fileName, final String engineName,\n                                                      final char[] password, final DataStoreType type) {\n\n        Instant start = Instant.now();\n\n        MessageBus.getInstance(engineName).setLocal();\n\n        final DataStore dataStore = type.getDataStore();\n\n        final Engine engine = dataStore.getLocalEngine(fileName, engineName, password);\n\n        if (engine != null) {\n            logger.info(ResourceUtils.getString(\"Message.EngineStart\"));\n            engineMap.put(engineName, engine);\n            dataStoreMap.put(engineName, dataStore);\n\n            Message message = new Message(MessageChannel.SYSTEM, ChannelEvent.FILE_LOAD_SUCCESS, engine);\n            MessageBus.getInstance(engineName).fireEvent(message);\n\n            if (engineName.equals(EngineFactory.DEFAULT)) {\n                Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n                pref.putBoolean(USED_PASSWORD, password.length > 0);\n                pref.put(LAST_DATABASE, fileName);\n                pref.putBoolean(LAST_REMOTE, false);\n            }\n\n            logger.log(Level.INFO, \"Boot time was {0} milliseconds\",\n                    ChronoUnit.MILLIS.between(start, Instant.now()));\n        }\n        return engine;\n    }\n\n    public static synchronized Engine bootClientEngine(final String host, final int port, final char[] password,\n                                                       final String engineName) {\n\n        if (engineMap.get(engineName) != null) {\n            throw new EngineException(\"A stale engine was found in the map\");\n        }\n\n        final Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n        Engine engine = null;\n\n        // start the client message bus\n        if (MessageBus.getInstance(engineName).setRemote(host, port + JpaNetworkServer.MESSAGE_SERVER_INCREMENT,\n                password)) {\n\n            pref.putInt(LAST_PORT, port);\n            pref.put(LAST_HOST, host);\n            pref.putBoolean(LAST_REMOTE, true);\n\n            final MessageBus messageBus = MessageBus.getInstance(engineName);\n\n            // after starting the remote message bus, it should receive the path on the server\n            final String remoteDataBasePath = messageBus.getRemoteDataBasePath();\n            final DataStoreType dataStoreType = messageBus.getRemoteDataStoreType();\n\n            if (remoteDataBasePath == null || remoteDataBasePath.isEmpty() || dataStoreType == null) {\n                throw new EngineException(\"Invalid connection wih the message bus\");\n            }\n\n            logger.log(Level.INFO, \"Remote path was {0}\", remoteDataBasePath);\n            logger.log(Level.INFO, \"Remote data store was {0}\", dataStoreType.name());\n            logger.log(Level.INFO, \"Engine name was {0}\", engineName);\n\n            DataStore dataStore = dataStoreType.getDataStore();\n\n            // connect to the remote server\n            engine = dataStore.getClientEngine(host, port, password, remoteDataBasePath);\n\n            if (engine != null) {\n                logger.info(ResourceUtils.getString(\"Message.EngineStart\"));\n\n                engineMap.put(engineName, engine);\n                dataStoreMap.put(engineName, dataStore);\n\n                // remember if the user used a password for the last session\n                pref.putBoolean(USED_PASSWORD, password.length > 0);\n\n                final Message message = new Message(MessageChannel.SYSTEM, ChannelEvent.FILE_LOAD_SUCCESS, engine);\n                MessageBus.getInstance(engineName).fireEvent(message);\n            }\n        }\n\n        return engine;\n    }\n\n    private static DataStoreType getDataStoreByType(final Path file) {\n        final FileType type = FileMagic.magic(file);\n\n        switch (type) {\n            case jGnash2XML:\n                return DataStoreType.XML;\n            case BinaryXStream:\n                return DataStoreType.BINARY_XSTREAM;\n            case h2:\n                return DataStoreType.H2_DATABASE;\n            case h2mv:\n                return DataStoreType.H2MV_DATABASE;\n            case hsql:\n                return DataStoreType.HSQL_DATABASE;\n            default:            \t\n            \tbreak;\n        }\n\n        return null;\n    }\n\n    public static DataStoreType getDataStoreByType(final String fileName) {\n        return getDataStoreByType(Paths.get(fileName));\n    }\n\n    public static float getFileVersion(final Path file, final char[] password) {\n        float version = 0;\n\n        final FileType type = FileMagic.magic(file);\n\n        switch (type) {\n            case jGnash2XML:\n                version = XMLDataStore.getFileVersion(file);\n                break;\n            case BinaryXStream:\n                version = BinaryXStreamDataStore.getFileVersion(file);\n                break;\n            case h2:\n            case h2mv:\n            case hsql:\n                try {\n                    version = SqlUtils.getFileVersion(file.toString(), password);\n                } catch (final Exception e) {\n                    version = 0;\n                }\n                break;\n            default:\n            \tbreak;\n        }\n\n        return version;\n    }\n\n    /**\n     * Returns the default path to the database without a file extension.\n     *\n     * @return Default path to the database\n     */\n    public static synchronized String getDefaultDatabase() {\n        final String base = System.getProperty(\"user.home\");\n        final String userName = System.getProperty(\"user.name\");\n\n        return base + FileUtils.SEPARATOR + DEFAULT_DIR + FileUtils.SEPARATOR + userName;\n    }\n\n    /**\n     * Returns the last open database. If a database has not been opened, then\n     * the default database will be returned.\n     *\n     * @return Last open or default database\n     */\n    public static synchronized String getLastDatabase() {\n        final Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n        return pref.get(LAST_DATABASE, getDefaultDatabase());\n    }\n\n    public static synchronized String getActiveDatabase() {\n        if (getLastRemote()) {\n            return REMOTE_PREFIX + getLastHost();\n        }\n\n        return getLastDatabase();\n    }\n\n    /**\n     * Returns the host of the last remote database connection. If a remote\n     * database has not been opened, then the default host will be returned.\n     *\n     * @return Last remote database host or default\n     */\n    public static synchronized String getLastHost() {\n        final Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n        return pref.get(LAST_HOST, EngineFactory.LOCALHOST);\n    }\n\n    /**\n     * Returns the port of the last remote database connection. If a remote\n     * database has not been opened, then the default port will be returned.\n     *\n     * @return Last remote database port or default\n     */\n    public static synchronized int getLastPort() {\n        final Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n        return pref.getInt(LAST_PORT, JpaNetworkServer.DEFAULT_PORT);\n    }\n\n    /**\n     * Returns true if the last connection was made to a remote host.\n     *\n     * @return true if the last connection was made to a remote host\n     */\n    public static synchronized boolean getLastRemote() {\n        final Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n        return pref.getBoolean(LAST_REMOTE, false);\n    }\n\n    public static synchronized boolean usedPassword() {\n        final Preferences pref = Preferences.userNodeForPackage(EngineFactory.class);\n\n        return pref.getBoolean(USED_PASSWORD, false);\n    }\n\n    /**\n     * Saves the active database as a new file/format\n     *\n     * @param destination new file\n     * @param percentCompleteConsumer progress consumer\n     * @throws IOException IO error\n     */\n    public static void saveAs(final String destination, final DoubleConsumer percentCompleteConsumer) throws IOException {\n\n        final String fileExtension = \".\" + FileUtils.getFileExtension(destination);\n        DataStoreType newFileType = DataStoreType.BINARY_XSTREAM;   // default for a new file\n\n        if (fileExtension.length() > 1) {   // should have more than just the period in it\n            for (final DataStoreType type : DataStoreType.values()) {\n                if (type.getDataStore().getFileExt().equalsIgnoreCase(fileExtension)) {\n                    newFileType = type;\n                    break;\n                }\n            }\n        }\n\n        final Path newFile = Paths.get(FileUtils.stripFileExtension(destination)\n                + newFileType.getDataStore().getFileExt());\n\n        final Path current = Paths.get(EngineFactory.getActiveDatabase());\n\n        // don't perform the save if the destination is going to overwrite the current database\n        if (!current.equals(newFile)) {\n            final DataStoreType currentType = dataStoreMap.get(EngineFactory.DEFAULT).getType();\n\n            // Need to create an interim copy when converting a relational database\n            if (currentType.supportsRemote && newFileType.supportsRemote) {\n                final Path tempFile = Files.createTempFile(\"jgnash-tmp\", BinaryXStreamDataStore.FILE_EXT);\n\n                Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n                if (engine != null) {\n                    // Get collection of object to persist\n                    Collection<StoredObject> objects = engine.getStoredObjects();\n\n                    // Write everything to a temporary file\n                    DataStoreType.BINARY_XSTREAM.getDataStore().saveAs(tempFile, objects, value -> {\n                        percentCompleteConsumer.accept(value * 0.5);   // doing it twice\n                    });\n\n                    // Close the current file\n                    EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n                    // Boot the engine using the temporary file\n                    EngineFactory.bootLocalEngine(tempFile.toString(), EngineFactory.DEFAULT,\n                            EngineFactory.EMPTY_PASSWORD);\n\n                    engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n                    if (engine != null) {\n\n                        // Get collection of object to persist\n                        objects = engine.getStoredObjects();\n\n                        // Write everything to the new file and close\n                        newFileType.getDataStore().saveAs(newFile, objects,\n                                value -> percentCompleteConsumer.accept(0.5 + value * 0.5));\n                        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n                        percentCompleteConsumer.accept(1);\n\n                        // Boot the engine with the new file\n                        EngineFactory.bootLocalEngine(newFile.toString(), EngineFactory.DEFAULT,\n                                EngineFactory.EMPTY_PASSWORD);\n                    }\n\n                    try {\n                        Files.delete(tempFile);\n                    } catch (final IOException ioe) {\n                        Logger.getLogger(EngineFactory.class.getName())\n                                .info(ResourceUtils.getString(\"Message.Error.RemoveTempFile\"));\n                    }\n                }\n            } else {    // Simple\n                Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n                if (engine != null) {\n                    final Collection<StoredObject> objects = engine.getStoredObjects();\n                    newFileType.getDataStore().saveAs(newFile, objects, percentCompleteConsumer);\n                    EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n                    EngineFactory.bootLocalEngine(newFile.toString(), EngineFactory.DEFAULT,\n                            EngineFactory.EMPTY_PASSWORD);\n                }\n            }\n        }\n    }\n\n    /**\n     * Saves a closed database as a new file/format\n     *\n     * @param fileName file to save a copy of\n     * @param newFileName new file\n     * @param password password\n     * @param percentCompleteConsumer progress consumer\n     * \n     * @throws IOException IO error\n     */\n    public static void saveAs(final String fileName, final String newFileName, final char[] password,\n                              final DoubleConsumer percentCompleteConsumer) throws IOException {\n\n        Objects.requireNonNull(fileName);\n        Objects.requireNonNull(newFileName);\n        Objects.requireNonNull(password);\n\n        final String ENGINE = UUID.randomUUID().toString();    // create a temporary engine ID for utility use only\n\n        final String fileExtension = \".\" + FileUtils.getFileExtension(newFileName);\n\n        DataStoreType newFileType = DataStoreType.BINARY_XSTREAM;   // default for a new file\n\n        // Determine the data store type given the file extension\n        if (fileExtension.length() > 1) {   // should have more than just the period in it\n            for (final DataStoreType type : DataStoreType.values()) {\n                if (type.getDataStore().getFileExt().equalsIgnoreCase(fileExtension)) {\n                    newFileType = type;\n                    break;\n                }\n            }\n        }\n\n        final Path newFile = Paths.get(FileUtils.stripFileExtension(newFileName)\n                + newFileType.getDataStore().getFileExt());\n\n        final Path current = Paths.get(fileName);\n\n        // don't perform the save if the destination is going to overwrite the current database\n        if (!current.equals(newFile)) {\n\n            // Need to know the data store type for correct behavior\n            final DataStoreType currentType = EngineFactory.getDataStoreByType(fileName);\n\n            Objects.requireNonNull(currentType);    // fail if type is null\n\n            // Create a utility engine instead of using the default\n            Engine engine = EngineFactory.bootLocalEngine(fileName, ENGINE, password);\n\n            if (currentType.supportsRemote && newFileType.supportsRemote) { // Relational database\n                final Path tempFile = Files.createTempFile(\"jgnash\", BinaryXStreamDataStore.FILE_EXT);\n\n                if (engine != null) {\n                    // Get collection of object to persist\n                    Collection<StoredObject> objects = engine.getStoredObjects();\n\n                    // Write everything to a temporary file\n                    DataStoreType.BINARY_XSTREAM.getDataStore().saveAs(tempFile, objects,  value -> {\n                        percentCompleteConsumer.accept(value * 0.5);   // doing it twice\n                    });\n                    EngineFactory.closeEngine(ENGINE);\n\n                    // Boot the engine with the temporary file\n                    engine = EngineFactory.bootLocalEngine(tempFile.toString(), ENGINE, EngineFactory.EMPTY_PASSWORD);\n\n                    if (engine != null) {\n\n                        // Get collection of object to persist\n                        objects = engine.getStoredObjects();\n\n                        // Write everything to the new file\n                        newFileType.getDataStore().saveAs(newFile, objects,\n                                value -> percentCompleteConsumer.accept(0.5 + value * 0.5));\n                        EngineFactory.closeEngine(ENGINE);\n\n                        // reset the password\n                        SqlUtils.changePassword(newFileName, EngineFactory.EMPTY_PASSWORD, password);\n                        percentCompleteConsumer.accept(1);\n                    }\n\n                    try {\n                        Files.delete(tempFile);\n                    } catch (final IOException ioe) {\n                        Logger.getLogger(EngineFactory.class.getName())\n                                .info(ResourceUtils.getString(\"Message.Error.RemoveTempFile\"));\n                    }\n                }\n            } else {    // Simple\n                if (engine != null) {\n                    final Collection<StoredObject> objects = engine.getStoredObjects();\n                    newFileType.getDataStore().saveAs(newFile, objects, percentCompleteConsumer);\n                    EngineFactory.closeEngine(ENGINE);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/ExchangeRate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Entity;\nimport javax.persistence.JoinTable;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OrderBy;\nimport javax.persistence.PostLoad;\n\nimport jgnash.util.Nullable;\n\n/**\n * Exchange rate object.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class ExchangeRate extends StoredObject {\n\n    @JoinTable\n    @OrderBy(\"date\")    //applying a sort order prevents refresh issues\n    @OneToMany(cascade = {CascadeType.ALL})\n    private final Set<ExchangeRateHistoryNode> historyNodes = new HashSet<>();\n\n    /**\n     * Cache the last exchange rate.\n     */\n    transient private BigDecimal lastRate;\n\n    /**\n     * Identifier for the ExchangeRate object.\n     */\n    private String rateId;\n\n    /**\n     * ReadWrite lock.\n     */\n    private transient ReadWriteLock lock = new ReentrantReadWriteLock();\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <p>\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public ExchangeRate() {\n    }\n\n    ExchangeRate(final String rateId) {\n        this.rateId = rateId;\n    }\n\n    public boolean contains(final ExchangeRateHistoryNode node) {\n\n        lock.readLock().lock();\n\n        try {\n            return historyNodes.contains(node);\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    public boolean contains(final LocalDate localDate) {\n\n        lock.readLock().lock();\n\n        boolean result = false;\n\n        try {\n            for (final ExchangeRateHistoryNode node : historyNodes) {\n                if (localDate.compareTo(node.getLocalDate()) == 0) {\n                    result = true;\n                    break;\n                }\n            }\n        } finally {\n            lock.readLock().unlock();\n        }\n\n        return result;\n    }\n\n    public List<ExchangeRateHistoryNode> getHistory() {\n        // return a defensive copy\n        List<ExchangeRateHistoryNode> nodes = new ArrayList<>(historyNodes);\n        Collections.sort(nodes);\n\n        return nodes;\n    }\n\n    boolean addHistoryNode(final ExchangeRateHistoryNode node) {\n        boolean result = false;\n\n        lock.writeLock().lock();\n\n        try {\n            historyNodes.add(node);\n\n            lastRate = null; // force an update\n\n            result = true;\n        } catch (final Exception ex) {\n            Logger.getLogger(ExchangeRate.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n        } finally {\n            lock.writeLock().unlock();\n        }\n\n        return result;\n    }\n\n    @Nullable\n    ExchangeRateHistoryNode getHistory(final LocalDate localDate) {\n        ExchangeRateHistoryNode node = null;\n\n        lock.readLock().lock();\n\n        try {\n            for (final ExchangeRateHistoryNode historyNode : historyNodes) {\n                if (localDate.compareTo(historyNode.getLocalDate()) == 0) {\n                    node = historyNode;\n                    break;\n                }\n            }\n        } finally {\n            lock.readLock().unlock();\n        }\n\n        return node;\n    }\n\n    boolean removeHistoryNode(final ExchangeRateHistoryNode hNode) {\n\n        lock.writeLock().lock();\n\n        try {\n            final boolean result = historyNodes.remove(hNode);\n\n            if (result) {\n                lastRate = null; // force an update\n            }\n\n            return result;\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    public String getRateId() {\n        return rateId;\n    }\n\n    public BigDecimal getRate() {\n        lock.readLock().lock();\n\n        try {\n            if (lastRate == null) {\n                if (!historyNodes.isEmpty()) {\n\n                    List<ExchangeRateHistoryNode> nodes = getHistory();\n\n                    lastRate = nodes.get(nodes.size() - 1).getRate();\n                } else {\n                    lastRate = BigDecimal.ONE;\n                }\n            }\n        } finally {\n            lock.readLock().unlock();\n        }\n\n        return lastRate;\n    }\n\n    /**\n     * Returns the exchange rate for a given {@code LocalDate}.\n     * <p>\n     * If a rate has not be set, {@code BigDecimal.ZERO} is returned\n     *\n     * @param localDate {@code LocalDate} for exchange\n     * @return the exchange rate if known, otherwise {@code BigDecimal.ZERO}\n     */\n    BigDecimal getRate(final LocalDate localDate) {\n        lock.readLock().lock();\n\n        BigDecimal rate = BigDecimal.ZERO;\n\n        try {\n            for (ExchangeRateHistoryNode historyNode : historyNodes) {\n                if (localDate.equals(historyNode.getLocalDate())) {\n                    rate = historyNode.getRate();\n                    break;\n                }\n            }\n        } finally {\n            lock.readLock().unlock();\n        }\n\n        return rate;\n    }\n\n    @Override\n    public boolean equals(final Object other) {\n        return this == other || other instanceof ExchangeRate && rateId.equals(((ExchangeRate) other).rateId);\n    }\n\n    @Override\n    public int hashCode() {\n        return super.hashCode() * 67 + rateId.hashCode();\n    }\n\n    /**\n     * Required by XStream for proper initialization.\n     *\n     * @return Properly initialized ExchangeRate\n     */\n    protected Object readResolve() {\n        postLoad();\n        return this;\n    }\n\n    @PostLoad\n    private void postLoad() {\n        lock = new ReentrantReadWriteLock(true);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/ExchangeRateDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.engine.dao.CommodityDAO;\n\n/**\n * DAO for exchange rate access.\n *\n * @author Craig Cavanaugh\n *\n */\nclass ExchangeRateDAO {\n\n    private final CommodityDAO commodityDAO;\n\n    ExchangeRateDAO(final CommodityDAO commodityDAO) {\n        this.commodityDAO = commodityDAO;\n    }\n\n    ExchangeRate getExchangeRateNode(final CurrencyNode baseCurrency, final CurrencyNode exchangeCurrency) {\n        if (baseCurrency.equals(exchangeCurrency)) {\n            return null;\n        }\n\n        final String rateId = Engine.buildExchangeRateId(baseCurrency, exchangeCurrency);\n\n        ExchangeRate node = commodityDAO.getExchangeNode(rateId);\n\n        if (node == null) {\n            node = new ExchangeRate(Engine.buildExchangeRateId(baseCurrency, exchangeCurrency));\n            commodityDAO.addExchangeRate(node);\n        }\n\n        return node;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/ExchangeRateHistoryNode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.util.NotNull;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.SequenceGenerator;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Objects;\n\n/**\n * Exchange rate history node for a {@code ExchangeRate}.\n * {@code ExchangeRateHistoryNode} objects are immutable.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class ExchangeRateHistoryNode implements Comparable<ExchangeRateHistoryNode>, Serializable {\n\n    @SuppressWarnings(\"UnusedDeclaration\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    public long id;\n\n    @Column(precision = 20, scale = 8)\n    private BigDecimal rate = BigDecimal.ZERO;\n\n    private LocalDate date = LocalDate.now();\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    protected ExchangeRateHistoryNode() {\n    }\n\n    /**\n     * Constructor.\n     *\n     * @param localDate date for this history node.  The date will be trimmed\n     * @param rate      exchange rate for the given date\n     */\n    ExchangeRateHistoryNode(final LocalDate localDate, final BigDecimal rate) {\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(rate);\n\n        this.date = localDate;\n        this.rate = rate;\n    }\n\n    public LocalDate getLocalDate() {\n        return date;\n    }\n\n    @Override\n    public int compareTo(@NotNull final ExchangeRateHistoryNode node) {\n        return getLocalDate().compareTo(node.getLocalDate());\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        return this == o || o instanceof ExchangeRateHistoryNode\n                && date.equals(((ExchangeRateHistoryNode) o).date);\n    }\n\n    @Override\n    public int hashCode() {\n        return date.hashCode();\n    }\n\n    public BigDecimal getRate() {\n        return rate;\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/InvestmentAccountProxy.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.concurrent.locks.Lock;\n\n/**\n * Investment Account Proxy class.\n *\n * @author Craig Cavanaugh\n */\nclass InvestmentAccountProxy extends AccountProxy {\n\n    public InvestmentAccountProxy(final Account account) {\n        super(account);\n    }\n\n    @Override\n    public BigDecimal getBalance(final LocalDate start, final LocalDate end) {\n        return getCashBalance(start, end).add(getMarketValue(start, end));\n    }\n\n    /**\n     * Returns the cash balance plus the market value of the shares.\n     *\n     * @return cash balance\n     */\n    @Override\n    public BigDecimal getBalance() {\n        return getCashBalance().add(getMarketValue());\n    }\n\n    @Override\n    public BigDecimal getBalance(final LocalDate date) {\n        return getCashBalance(date).add(getMarketValue(date));\n    }\n\n    /**\n     * Returns the cash balance of this account.  Cash balance may be referred to as the \"sweep\" account where\n     * the money market fund (cash) does not have it's own account number and the user see's it as a cash balance\n     * in their account statements.\n     * \n     * @return cash balance of the account\n     */\n    @Override\n    public BigDecimal getCashBalance() {\n        return super.getBalance();\n    }\n\n    /**\n     * Get the account's cash balance up to a specified index.\n     *\n     * @param index the balance of the account at the specified index.\n     * @return the balance of the account at the specified index.\n     */\n    private BigDecimal getCashBalanceAt(final int index) {\n        return super.getBalanceAt(index);\n    }\n\n    /**\n     * Returns the balance of the transactions inclusive of the start and end dates.\n     * <p>\n     * The balance includes the cash transactions and is based on the current market value.\n     *\n     * @param start The inclusive start date\n     * @param end   The inclusive end date\n     * @return The ending balance\n     */\n    private BigDecimal getCashBalance(final LocalDate start, final LocalDate end) {\n        return super.getBalance(start, end);\n    }\n\n    /**\n     * Returns the cash account balance up to and inclusive of the supplied date.\n     *\n     * @param end The inclusive ending date\n     * @return The ending cash balance\n     */\n    private BigDecimal getCashBalance(final LocalDate end) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            return !account.transactions.isEmpty()\n                    ? getCashBalance(account.getSortedTransactionList().get(0).getLocalDate(), end) : BigDecimal.ZERO;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns a market price for the supplied {@code SecurityNode} that is closest to the supplied date without\n     * exceeding it. The history of the {@code SecurityNode} is searched as well as the account's transaction\n     * history to find the closest market price without exceeding the supplied date.\n     *\n     * @param node security to search against\n     * @param date date to search against\n     * @return market price\n     */\n    private BigDecimal getMarketPrice(final SecurityNode node, final LocalDate date) {\n        account.getTransactionLock().readLock().lock();\n\n        try {\n            return Engine.getMarketPrice(account.getSortedTransactionList(), node, account.getCurrencyNode(), date);\n        } finally {\n            account.getTransactionLock().readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the market value of this account.\n     *\n     * @return the market value of the account\n     */\n    @Override\n    public BigDecimal getMarketValue() {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n\n            BigDecimal marketValue = BigDecimal.ZERO;\n\n            int count = account.getTransactionCount();\n\n            if (count > 0) {\n                LocalDate lastDate = account.getSortedTransactionList().get(count - 1).getLocalDate();\n\n                /*\n                 * If the user was to enter a date value greater than the current date, then\n                 * \"new Date()\" is not sufficient to pick up the last transaction.  If the\n                 * current date is greater, than it is used to force use of the latest\n                 * security price.\n                 */\n\n                final LocalDate startDate = account.getSortedTransactionList().get(0).getLocalDate();\n\n                if (lastDate.compareTo(LocalDate.now()) >= 0) {\n                    marketValue = getMarketValue(startDate, lastDate);\n                } else {\n                    marketValue = getMarketValue(startDate, LocalDate.now());\n                }\n            }\n\n            return marketValue;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns the market value of the account at a specified date. The closest market price is used and only investment\n     * transactions earlier and inclusive of the specified date are considered.\n     *\n     * @param date the end date to calculate the market value\n     * @return the ending balance\n     */\n    private BigDecimal getMarketValue(final LocalDate date) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            BigDecimal marketValue = BigDecimal.ZERO;\n\n            if (account.getTransactionCount() > 0) {\n                marketValue = getMarketValue(account.getSortedTransactionList().get(0).getLocalDate(), date);\n            }\n\n            return marketValue;\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Returns the market value for an account.\n     *\n     * @param start inclusive start date\n     * @param end   inclusive end date\n     * @return market value\n     */\n    private BigDecimal getMarketValue(final LocalDate start, final LocalDate end) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            final HashMap<SecurityNode, BigDecimal> priceMap = new HashMap<>();\n\n            // build lookup map for market prices\n            for (final SecurityNode node : account.getSecurities()) {\n                priceMap.put(node, getMarketPrice(node, end));\n            }\n\n            BigDecimal balance = BigDecimal.ZERO;\n\n            // Get a defensive copy, JPA lazy updates can have side effects\n            List<Transaction> transactions = account.getSortedTransactionList();\n\n            for (final Transaction t : transactions) {\n                if (t.getLocalDate().compareTo(start) >= 0 && t.getLocalDate().compareTo(end) <= 0) {\n                    if (t instanceof InvestmentTransaction) {\n                        balance = balance.add(((InvestmentTransaction) t).getMarketValue(priceMap.get(((InvestmentTransaction) t).getSecurityNode())));\n                    }\n                }\n            }\n\n            return round(balance);\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Calculates the accounts market value based on the latest security price.\n     *\n     * @param index index to calculate the balance to\n     * @return market value\n     */\n    private BigDecimal getMarketValueAt(final int index) {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            final HashMap<SecurityNode, BigDecimal> priceMap = new HashMap<>();\n\n            LocalDate today = LocalDate.now();\n\n            // build lookup map for market prices\n            for (final SecurityNode node : account.getSecurities()) {\n                priceMap.put(node, getMarketPrice(node, today));\n            }\n\n            BigDecimal balance = BigDecimal.ZERO;\n\n            for (int i = 0; i <= index; i++) {\n                Transaction t = account.getTransactionAt(i);\n\n                if (t instanceof InvestmentTransaction) {\n                    balance = balance.add(((InvestmentTransaction) t).getMarketValue(priceMap.get(((InvestmentTransaction) t).getSecurityNode())));\n                }\n            }\n\n            return round(balance);\n        } finally {\n            l.unlock();\n        }\n    }\n\n    private BigDecimal getReconciledMarketValue() {\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            final HashMap<SecurityNode, BigDecimal> priceMap = new HashMap<>();\n\n            // build lookup map for market prices\n            for (final SecurityNode node :account.getSecurities()) {\n                priceMap.put(node, getMarketPrice(node, LocalDate.now()));\n            }\n\n            BigDecimal balance = BigDecimal.ZERO;\n\n            // Get a defensive copy, JPA lazy updates can have side effects\n            List<Transaction> transactions = account.getSortedTransactionList();\n\n            for (final Transaction t : transactions) {\n                if (t instanceof InvestmentTransaction && t.getReconciled(account) == ReconciledState.RECONCILED) {\n                    balance = balance.add(((InvestmentTransaction) t).getMarketValue(priceMap.get(((InvestmentTransaction) t).getSecurityNode())));\n                }\n            }\n\n            return round(balance);\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Calculates the reconciled balance of the account.\n     *\n     * @return the reconciled balance of this account\n     */\n    @Override\n    public BigDecimal getReconciledBalance() {\n        return super.getReconciledBalance().add(getReconciledMarketValue());\n    }\n\n    /**\n     * Get the default opening balance for reconciling the account.\n     *\n     * @return Opening balance for reconciling the account\n     */\n    @Override\n    public BigDecimal getOpeningBalanceForReconcile() {\n\n        final Lock l = account.getTransactionLock().readLock();\n        l.lock();\n\n        try {\n            final LocalDate date = account.getFirstUnreconciledTransactionDate();\n\n            BigDecimal balance = BigDecimal.ZERO;\n\n            final List<Transaction> transactions = account.getSortedTransactionList();\n\n            for (int i = 0; i < transactions.size(); i++) {\n                if (transactions.get(i).getLocalDate().equals(date)) {\n                    if (i > 0) {\n                        balance = getCashBalanceAt(i - 1).add(getMarketValueAt(i - 1));\n                    }\n                    break;\n                }\n            }\n\n            return round(balance);\n        } finally {\n            l.unlock();\n        }\n    }\n\n    /**\n     * Scales / Rounds a given value to the scale of the accounts currency.  Calculating Market value will result\n     * in minor discrepancies.  Use this before returning values for consistent calculations.\n     *\n     * @param value value to round\n     * @return rounded value\n     */\n    private BigDecimal round(final BigDecimal value) {\n        return value.setScale(account.getCurrencyNode().getScale(), MathConstants.roundingMode);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/InvestmentPerformanceSummary.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeMap;\n\nimport org.apache.commons.lang3.tuple.ImmutablePair;\nimport org.apache.commons.lang3.tuple.Pair;\n\n/**\n * Investment Performance Summary Class.\n * \n * @author Craig Cavanaugh\n */\npublic class InvestmentPerformanceSummary {\n\n    private final Account account;\n\n    private LocalDate startDate;\n\n    private LocalDate endDate;\n\n    private final Map<SecurityNode, SecurityPerformanceData> performanceData = new TreeMap<>();\n    \n    private final List<Transaction> transactions;\n\n    private final CurrencyNode baseCurrency;\n\n    /**\n     * If true, recurse into sub accounts\n     */\n    private final boolean recursive;\n\n    public InvestmentPerformanceSummary(final Account account, final LocalDate startDate, final LocalDate endDate,\n                                         final boolean recursive) {\n        Objects.requireNonNull(account, \"Account may not be null\");\n\n        this.recursive = recursive;\n\n        if (!account.memberOf(AccountGroup.INVEST)) {\n            throw new IllegalArgumentException(\"The account is not a valid type\");\n        }\n\n        this.baseCurrency = account.getCurrencyNode();\n        this.account = account;\n\n        if (startDate == null || endDate == null) {\n\n            final Pair<LocalDate, LocalDate> datePair = getTransactionDateRange(account, recursive);\n\n            setStartDate(datePair.getLeft());\n            setEndDate(datePair.getRight());\n        } else {\n            setStartDate(startDate);\n            setEndDate(endDate);\n        }\n\n        transactions = account.getTransactions(getStartDate(), getEndDate());\n\n        if (recursive && account.getChildCount() > 0) {\n            collectSubAccountTransactions(account, transactions);\n        }\n\n        Collections.sort(transactions);\n    }\n\n    public static Pair<LocalDate, LocalDate> getTransactionDateRange(final Account account, final boolean recursive) {\n        List<Transaction> transactions = new ArrayList<>(account.getSortedTransactionList());\n\n        if (recursive && account.getChildCount() > 0) {\n            _collectSubAccountTransactions(account, transactions);\n        }\n\n        transactions.sort(null);\n\n        return new ImmutablePair<>(transactions.get(0).getLocalDate(),\n                transactions.get(transactions.size() - 1).getLocalDate());\n    }\n\n    private static void _collectSubAccountTransactions(final Account account, final List<Transaction> transactions) {\n        for (final Account child : account.getChildren(Comparators.getAccountByCode())) {\n            transactions.addAll(child.getSortedTransactionList());\n\n            if (child.getChildCount() > 0) {\n                _collectSubAccountTransactions(child, transactions);\n            }\n        }\n    }\n\n    private void collectSubAccountTransactions(final Account account, final List<Transaction> transactions) {\n        for (final Account child : account.getChildren(Comparators.getAccountByCode())) {\n            transactions.addAll(child.getTransactions(getStartDate(), getEndDate()));\n\n            if (child.getChildCount() > 0) {\n                collectSubAccountTransactions(child, transactions);\n            }\n        }\n    }\n\n    private void collectSubAccountSecurities(final Account account, final Set<SecurityNode> securities) {\n        for (final Account child : account.getChildren(Comparators.getAccountByCode())) {\n            securities.addAll(child.getSecurities());\n\n            if (child.getChildCount() > 0) {\n                collectSubAccountSecurities(child, securities);\n            }\n        }\n    }\n\n    public SecurityPerformanceData getPerformanceData(final SecurityNode node) {\n        return performanceData.get(node);\n    }\n\n    public List<SecurityNode> getSecurities() {\n        return new ArrayList<>(performanceData.keySet());\n    }\n\n    /**\n     * Calculates the cost basis of a given security which is the average cost including fees.\n     * \n     * @param data SecurityPerformanceData object to save the result in\n     * @param transactions transactions to calculate cost basis against\n     */\n    private void calculateCostBasis(final SecurityPerformanceData data, final List<Transaction> transactions) {\n        SecurityNode node = data.getNode();\n\n        BigDecimal totalShares = BigDecimal.ZERO;\n        BigDecimal totalCost = BigDecimal.ZERO;\n\n        for (Transaction transaction : transactions) {\n            if (transaction instanceof InvestmentTransaction) {\n                InvestmentTransaction t = (InvestmentTransaction) transaction;\n\n                if (t.getSecurityNode().equals(node)) {\n\n                    BigDecimal rate = baseCurrency.getExchangeRate(t.getInvestmentAccount().getCurrencyNode());\n\n                    BigDecimal fees = t.getFees().multiply(rate);\n                    BigDecimal quantity = t.getQuantity();\n                    BigDecimal price = t.getPrice().multiply(rate);\n\n                    switch (t.getTransactionType()) {\n                        case BUYSHARE:\n                        case REINVESTDIV:\n                            totalShares = totalShares.add(quantity);\n                            totalCost = totalCost.add(price.multiply(quantity).add(fees));\n                            break;\n                        case SPLITSHARE:\n                            totalShares = totalShares.add(quantity);\n                            break;\n                        case MERGESHARE:\n                            totalShares = totalShares.subtract(quantity);\n                            break;\n                        case DIVIDEND:\n                            // do nothing, no fees, no shares added.\n                            break;\n                        default:\n                            break;\n                    }\n                }\n            }\n        }\n\n        if (totalShares.compareTo(BigDecimal.ZERO) != 0) {\n            data.setCostBasisShares(totalShares);\n            data.setCostBasisPerShare(totalCost.divide(totalShares, MathConstants.mathContext));\n        }\n    }\n\n    /**\n     * Calculates the realized gains of a given Security.\n     * \n     * @param data SecurityPerformanceData object to save the result in\n     * @param transactions transactions to calculate the realized gains\n     */\n    private void calculateRealizedGains(final SecurityPerformanceData data, final List<Transaction> transactions) {\n        SecurityNode node = data.getNode();\n\n        BigDecimal totalSharesSold = BigDecimal.ZERO;\n        BigDecimal totalSales = BigDecimal.ZERO;\n\n        for (Transaction transaction : transactions) {\n            if (transaction instanceof InvestmentTransaction) {\n                InvestmentTransaction t = (InvestmentTransaction) transaction;\n\n                if (t.getSecurityNode().equals(node)) {\n\n                    BigDecimal rate = baseCurrency.getExchangeRate(t.getInvestmentAccount().getCurrencyNode());\n\n                    BigDecimal fees = t.getFees().multiply(rate);\n                    BigDecimal quantity = t.getQuantity();\n                    BigDecimal price = t.getPrice().multiply(rate);\n\n                    switch (t.getTransactionType()) {\n                        case SELLSHARE:\n                            totalSharesSold = totalSharesSold.add(quantity);\n                            totalSales = totalSales.add(price.multiply(quantity).subtract(fees));\n                            break;\n                        case DIVIDEND:\n                            totalSales = totalSales.add(t.getTotalWithoutCashTransfer(t.getInvestmentAccount()).multiply(rate));\n                            break;\n                        case REINVESTDIV:\n                            totalSales = totalSales.add(t.getTotalWithoutCashTransfer(t.getInvestmentAccount()).multiply(rate)).subtract(fees);\n                            break;\n                        default:\n                            break;\n                    }\n                }\n            }\n        }\n\n        if (totalSharesSold.compareTo(BigDecimal.ZERO) != 0) {\n            data.setAvgSalePrice(totalSales.divide(totalSharesSold, MathConstants.mathContext));\n            data.setRealizedGains(data.getAvgSalePrice().subtract(data.getCostBasisPerShare()).multiply(totalSharesSold));\n\n        } else if (totalSales.compareTo(BigDecimal.ZERO) != 0) { // pure dividends and no share purchased or sold\n            data.setRealizedGains(totalSales);\n        }\n    }\n\n    private static void calculateUnrealizedGains(final SecurityPerformanceData data) {\n        if (data.getSharesHeld().compareTo(BigDecimal.ZERO) != 0) {\n            data.setUnrealizedGains(data.getPrice().subtract(data.getCostBasisPerShare()).multiply(data.getSharesHeld()));\n        }\n    }\n\n    private static void calculateTotalGains(final SecurityPerformanceData data) {\n        data.setTotalGains(data.getUnrealizedGains().add(data.getRealizedGains()));\n\n        if (data.getTotalCostBasis().compareTo(BigDecimal.ZERO) != 0) {\n            data.setTotalGainsPercentage(data.getTotalGains().divide(data.getTotalCostBasis(), MathConstants.mathContext));\n        }\n    }\n\n    /**\n     * Sums the number of given security shares.\n     * \n     * @param data SecurityPerformanceData object to save the result in\n     * @param transactions transactions to count shares against\n     */\n    private static void calculateShares(final SecurityPerformanceData data, final List<Transaction> transactions) {\n        SecurityNode node = data.getNode();\n\n        BigDecimal shares = BigDecimal.ZERO;\n\n        for (Transaction transaction : transactions) {\n            if (transaction instanceof InvestmentTransaction) {\n                InvestmentTransaction t = (InvestmentTransaction) transaction;\n\n                if (t.getSecurityNode().equals(node)) {\n                    switch (t.getTransactionType()) {\n                        case ADDSHARE:\n                        case BUYSHARE:\n                        case REINVESTDIV:\n                        case SPLITSHARE:\n                            shares = shares.add(t.getQuantity());\n                            break;\n                        case REMOVESHARE:\n                        case SELLSHARE:\n                        case MERGESHARE:\n                            shares = shares.subtract(t.getQuantity());\n                            break;\n                        default:\n                            break;\n                    }\n                }\n            }\n        }\n\n        data.setSharesHeld(data.getSharesHeld().add(shares).setScale(MathConstants.SECURITY_QUANTITY_ACCURACY, MathConstants.roundingMode));\n    }\n\n    private void calculatePercentPortfolio() {\n        BigDecimal marketValue = BigDecimal.ZERO;\n\n        for (SecurityPerformanceData data : performanceData.values()) {\n            marketValue = marketValue.add(data.getMarketValue(account.getCurrencyNode()));\n        }\n\n        for (SecurityPerformanceData data : performanceData.values()) {\n            if (data.getMarketValue().compareTo(BigDecimal.ZERO) != 0) {\n                BigDecimal percentage = data.getMarketValue(account.getCurrencyNode()).divide(marketValue, MathConstants.mathContext);\n\n                data.setPercentPortfolio(percentage);\n            }\n        }\n    }\n\n    /**\n     * Calculates the internal rate of return of a given security.\n     * \n     * @param data SecurityPerformanceData object to save the result in\n     * @param transactions transactions to obtain the cash flow\n     */\n    private void calculateInternalRateOfReturn(final SecurityPerformanceData data, final List<Transaction> transactions) {\n        SecurityNode node = data.getNode();\n\n        CashFlow cashFlow = new CashFlow();\n        \n        BigDecimal totalShares = BigDecimal.ZERO;\n\n        for (Transaction transaction : transactions) {\n            if (transaction instanceof InvestmentTransaction) {\n                InvestmentTransaction t = (InvestmentTransaction) transaction;\n\n                if (t.getSecurityNode().equals(node)) {\n\n                    BigDecimal rate = baseCurrency.getExchangeRate(t.getInvestmentAccount().getCurrencyNode());\n\n                    BigDecimal fees = t.getFees().multiply(rate);\n                    BigDecimal quantity = t.getQuantity();\n                    BigDecimal price = t.getPrice().multiply(rate);\n\n                    switch (t.getTransactionType()) {\n                        case BUYSHARE:\n                            totalShares = totalShares.add(quantity);\n                            cashFlow.add(t.getLocalDate(), price.multiply(quantity).add(fees).negate());\n                            break;\n                        case SELLSHARE:\n                            totalShares = totalShares.subtract(quantity);\n                            cashFlow.add(t.getLocalDate(), price.multiply(quantity).subtract(fees));\n                            break;\n                        case SPLITSHARE:\n                        case ADDSHARE:\n                            totalShares = totalShares.add(quantity);\n                            break;\n                        case MERGESHARE:\n                        case REMOVESHARE:\n                            totalShares = totalShares.subtract(quantity);\n                            break;\n                        case DIVIDEND:\n                        case RETURNOFCAPITAL:\n                            cashFlow.add(t.getLocalDate(), t.getTotalWithoutCashTransfer(t.getInvestmentAccount()).multiply(rate));\n                            break;\n                        case REINVESTDIV:\n                            totalShares = totalShares.add(quantity);\n                            cashFlow.add(t.getLocalDate(), t.getTotalWithoutCashTransfer(t.getInvestmentAccount()).multiply(rate));\n                            break;\n                        default:\n                            break;\n                    }\n                }\n            }\n        }\n\n        // unrealized gains\n        cashFlow.add(getEndDate(), totalShares.multiply(getMarketPrice(node, getEndDate())));\n        \n        data.setInternalRateOfReturn(cashFlow.internalRateOfReturn());\n    }\n\n    public void runCalculations() {\n\n        Set<SecurityNode> nodes = account.getSecurities();\n\n        if (recursive) {\n            collectSubAccountSecurities(account, nodes);\n        }\n\n        for (final SecurityNode node : nodes) {\n            SecurityPerformanceData data = new SecurityPerformanceData(node);\n\n            data.setPrice(getMarketPrice(node, getEndDate()));\n\n            performanceData.put(node, data);\n\n            calculateShares(data, transactions);\n            calculateCostBasis(data, transactions);\n\n            calculateRealizedGains(data, transactions);\n            calculateUnrealizedGains(data);\n\n            calculateTotalGains(data);\n            \n            calculateInternalRateOfReturn(data, transactions);\n        }\n\n        calculatePercentPortfolio();\n    }\n\n    private BigDecimal getMarketPrice(final SecurityNode node, final LocalDate date) {\n        return Engine.getMarketPrice(transactions, node, baseCurrency, date);\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder b = new StringBuilder();\n\n        final NumberFormat percentageFormat = NumberFormat.getPercentInstance();\n        percentageFormat.setMinimumFractionDigits(2);\n        \n        final String lineSep = System.lineSeparator();\n        \n        for (SecurityPerformanceData data : performanceData.values()) {\n            b.append(data.getNode().getSymbol()).append(lineSep);\n            b.append(\"sharesHeld: \").append(data.getSharesHeld().toPlainString()).append(lineSep);\n            b.append(\"price: \").append(data.getPrice().toPlainString()).append(lineSep);\n            b.append(\"costBasisPerShare: \").append(data.getCostBasisPerShare().toPlainString()).append(lineSep);\n            b.append(\"costBasisShares: \").append(data.getCostBasisShares().toPlainString()).append(lineSep);\n            b.append(\"totalCostBasis: \").append(data.getTotalCostBasis().toPlainString()).append(lineSep);\n            b.append(\"heldCostBasis: \").append(data.getHeldCostBasis().toPlainString()).append(lineSep);\n            b.append(\"marketValue: \").append(data.getMarketValue().toPlainString()).append(lineSep);\n            b.append(\"unrealizedGains: \").append(data.getUnrealizedGains().toPlainString()).append(lineSep);\n            b.append(\"realizedGains: \").append(data.getRealizedGains().toPlainString()).append(lineSep);\n            b.append(\"totalGains: \").append(data.getTotalGains().toPlainString()).append(lineSep);\n            b.append(\"totalGainsPercentage: \").append(percentageFormat.format(data.getTotalGainsPercentage())).append(lineSep);\n            b.append(\"sharesSold: \").append(data.getSharesSold().toPlainString()).append(lineSep);\n            b.append(\"avgSalePrice: \").append(data.getAvgSalePrice().toPlainString()).append(lineSep);\n            b.append(\"percentPortfolio: \").append(percentageFormat.format(data.getPercentPortfolio())).append(lineSep);\n            b.append(\"internalRateOfReturn: \").append(percentageFormat.format(data.getInternalRateOfReturn())).append(lineSep);\n            b.append(lineSep);\n        }\n\n        return b.toString();\n    }\n\n    private void setStartDate(LocalDate startDate) {\n        this.startDate = startDate;\n    }\n\n    private LocalDate getStartDate() {\n        return startDate;\n    }\n\n    private void setEndDate(LocalDate endDate) {\n        this.endDate = endDate;\n    }\n\n    private LocalDate getEndDate() {\n        return endDate;\n    }\n\n    public class SecurityPerformanceData {\n\n        private SecurityNode node;\n\n        private BigDecimal sharesHeld = BigDecimal.ZERO;\n\n        private BigDecimal costBasisPerShare = BigDecimal.ZERO;\n\n        private BigDecimal costBasisShares = BigDecimal.ZERO;\n\n        private BigDecimal avgSalePrice = BigDecimal.ZERO;\n\n        private BigDecimal price = BigDecimal.ZERO;\n\n        private BigDecimal realizedGains = BigDecimal.ZERO;\n\n        private BigDecimal unrealizedGains = BigDecimal.ZERO;\n\n        private BigDecimal totalGains = BigDecimal.ZERO;\n\n        private BigDecimal totalGainsPercentage = BigDecimal.ZERO;\n\n        private BigDecimal percentPortfolio = BigDecimal.ZERO;\n        \n        private double internalRateOfReturn = 0.;\n\n        SecurityPerformanceData(final SecurityNode node) {\n            setNode(node);\n        }\n\n        public BigDecimal getCostBasisPerShare() {\n            return costBasisPerShare;\n        }\n\n        public BigDecimal getCostBasisShares() {\n            return costBasisShares;\n        }\n\n        public BigDecimal getMarketValue() {\n            return getPrice().multiply(getSharesHeld(), MathConstants.mathContext);\n        }\n\n        public BigDecimal getMarketValue(final CurrencyNode currencyNode) {\n            return getPrice(currencyNode).multiply(getSharesHeld(), MathConstants.mathContext);\n        }\n\n        public SecurityNode getNode() {\n            return node;\n        }\n\n        public BigDecimal getSharesHeld() {\n            return sharesHeld;\n        }\n\n        public BigDecimal getPrice(final CurrencyNode currencyNode) {\n            return price.multiply(account.getCurrencyNode().getExchangeRate(currencyNode));\n        }\n\n        public BigDecimal getPrice() {\n            return price;\n        }\n\n        public BigDecimal getRealizedGains() {\n            return realizedGains;\n        }\n\n        public BigDecimal getTotalCostBasis() {\n            return getCostBasisPerShare().multiply(getCostBasisShares(), MathConstants.mathContext);\n        }\n\n        public BigDecimal getHeldCostBasis() {\n            return getCostBasisPerShare().multiply(getSharesHeld(), MathConstants.mathContext);\n        }\n\n        public BigDecimal getTotalGains() {\n            return totalGains;\n        }\n\n        public BigDecimal getTotalGainsPercentage() {\n            return totalGainsPercentage;\n        }\n\n        public BigDecimal getUnrealizedGains() {\n            return unrealizedGains;\n        }\n\n        public double getInternalRateOfReturn() {\n            return internalRateOfReturn;\n        }\n\n        void setCostBasisPerShare(final BigDecimal costBasis) {\n            this.costBasisPerShare = costBasis;\n        }\n\n        void setCostBasisShares(final BigDecimal costBasisShares) {\n            this.costBasisShares = costBasisShares;\n        }\n\n        private void setNode(final SecurityNode node) {\n            this.node = node;\n        }\n\n        void setSharesHeld(final BigDecimal sharesHeld) {\n            this.sharesHeld = sharesHeld;\n        }\n\n        void setPrice(final BigDecimal price) {\n            this.price = price;\n        }\n\n        void setAvgSalePrice(final BigDecimal avgSalePrice) {\n            this.avgSalePrice = avgSalePrice;\n        }\n\n        void setUnrealizedGains(final BigDecimal unrealizedGains) {\n            this.unrealizedGains = unrealizedGains;\n        }\n\n        void setRealizedGains(final BigDecimal realizedGains) {\n            this.realizedGains = realizedGains;\n        }\n\n        void setTotalGains(final BigDecimal totalGains) {\n            this.totalGains = totalGains;\n        }\n\n        void setTotalGainsPercentage(final BigDecimal totalGainsPercentage) {\n            this.totalGainsPercentage = totalGainsPercentage;\n        }\n\n        public BigDecimal getAvgSalePrice() {\n            return avgSalePrice;\n        }\n\n        public BigDecimal getSharesSold() {\n            return getCostBasisShares().subtract(getSharesHeld());\n        }\n\n        public BigDecimal getPercentPortfolio() {\n            return percentPortfolio;\n        }\n\n        public void setPercentPortfolio(final BigDecimal percentPortfolio) {\n            this.percentPortfolio = percentPortfolio;\n        }\n\n        public void setInternalRateOfReturn(final double internalRateOfReturn) {\n            this.internalRateOfReturn = internalRateOfReturn;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/InvestmentTransaction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.List;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Class for investment transactions.\n * <p>\n * All TransactionEntry(s) must be of the same security.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class InvestmentTransaction extends Transaction {\n\n    public InvestmentTransaction() {\n        super();\n    }\n\n    /**\n     * Returns the transaction type.  If a valid investment entry has not been added then\n     * {@code TransactionType.INVALID} will be returned.\n     *\n     * @return transaction type\n     */\n    @NotNull\n    @Override\n    public TransactionType getTransactionType() {\n        TransactionType type = TransactionType.INVALID;\n\n        for (TransactionEntry e : transactionEntries) {\n            if (e instanceof AbstractInvestmentTransactionEntry) {\n                type = ((AbstractInvestmentTransactionEntry) e).getTransactionType();\n                break;\n            }\n        }\n\n        return type;\n    }\n\n    public Account getInvestmentAccount() {\n\n        Account account = null;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e.getCreditAccount().getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                account = e.getCreditAccount();\n            } else if (e.getDebitAccount().getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                account = e.getDebitAccount();\n            }\n        }\n\n        return account;\n    }\n\n    @Override\n    public void addTransactionEntry(@NotNull final TransactionEntry entry) {\n        if (entry instanceof AbstractInvestmentTransactionEntry) {\n\n            // assert that the types are a match if established\n            if (getTransactionType() != TransactionType.INVALID\n                    && ((AbstractInvestmentTransactionEntry) entry).getTransactionType() != getTransactionType()) {\n                throw new IllegalArgumentException(\"TransactionEntry type did not match the transaction type\");\n            }\n\n            // assert that the securities are a match if established\n            if (getSecurityNode() != null\n                    && !((AbstractInvestmentTransactionEntry) entry).getSecurityNode().equals(getSecurityNode())) {\n                throw new IllegalArgumentException(\"TransactionEntry security did not match the transaction security\");\n            }\n        }\n        super.addTransactionEntry(entry);\n    }\n\n    /**\n     * Returns the price per share.\n     *\n     * @return the price per share\n     */\n    public BigDecimal getPrice() {\n        BigDecimal price = BigDecimal.ZERO;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e instanceof AbstractInvestmentTransactionEntry) {\n                price = ((AbstractInvestmentTransactionEntry) e).getPrice();\n                break;\n            }\n        }\n\n        return price;\n    }\n\n    /**\n     * Returns the number of shares assigned to this transaction.\n     *\n     * @return the quantity of securities for this transaction\n     * @see #getSignedQuantity()\n     */\n    public BigDecimal getQuantity() {\n        BigDecimal quantity = BigDecimal.ZERO;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e instanceof AbstractInvestmentTransactionEntry) {\n                quantity = quantity.add(((AbstractInvestmentTransactionEntry) e).getQuantity());\n            }\n        }\n\n        return quantity;\n    }\n\n    /**\n     * Returns the number of shares assigned to this transaction.\n     *\n     * @return the quantity of securities for this transaction\n     * @see #getSignedQuantity()\n     */\n    private BigDecimal getSignedQuantity() {\n        BigDecimal quantity = BigDecimal.ZERO;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e instanceof AbstractInvestmentTransactionEntry) {\n                quantity = quantity.add(((AbstractInvestmentTransactionEntry) e).getSignedQuantity());\n            }\n        }\n\n        return quantity;\n    }\n\n    public SecurityNode getSecurityNode() {\n        SecurityNode node = null;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e instanceof AbstractInvestmentTransactionEntry) {\n                node = ((AbstractInvestmentTransactionEntry) e).getSecurityNode();\n            }\n        }\n\n        return node;\n    }\n\n    /**\n     * Sum transaction fees.\n     *\n     * @return transaction fees\n     */\n    public BigDecimal getFees() {\n        return getFees(getInvestmentAccount());\n    }\n\n    /**\n     * Sum transaction fees for a given {@code Account}.\n     *\n     * @param account account to calculate fees against\n     * @return transaction fees\n     */\n    private BigDecimal getFees(final Account account) {\n        BigDecimal fees = BigDecimal.ZERO;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e.getTransactionTag() == TransactionTag.INVESTMENT_FEE) {\n                fees = fees.add(e.getAmount(account));\n            }\n        }\n\n        return fees.negate();\n    }\n\n    /**\n     * Get a list of transaction entries tagged as investment fees.\n     *\n     * @return list of investment fees\n     * @see TransactionTag#INVESTMENT_FEE\n     */\n    public List<TransactionEntry> getInvestmentFeeEntries() {\n        return getTransactionEntriesByTag(TransactionTag.INVESTMENT_FEE);\n    }\n\n    /**\n     * Get a list of transaction entries tagged as gains and loss.\n     *\n     * @return list of gains and loss entries\n     * @see TransactionTag#GAIN_LOSS\n     */\n    public List<TransactionEntry> getInvestmentGainLossEntries() {\n        return getTransactionEntriesByTag(TransactionTag.GAIN_LOSS);\n    }\n\n    /**\n     * Return the market value of the transaction based on the supplied share price.\n     *\n     * @param sharePrice share price\n     * @return the value of this transaction\n     */\n    public BigDecimal getMarketValue(final BigDecimal sharePrice) {\n        return getSignedQuantity().multiply(sharePrice);\n    }\n\n    /**\n     * Return the market value of the transaction based on the latest share price.\n     *\n     * @param date Date to base market value against\n     * @return the value of this transaction\n     */\n    public BigDecimal getMarketValue(final LocalDate date) {\n        return getSignedQuantity().multiply(\n                getSecurityNode().getMarketPrice(date, getInvestmentAccount().getCurrencyNode()));\n    }\n\n    /**\n     * Calculates the total of the value of the shares, gains, fees, etc. as it\n     * pertains to an account.\n     * <p>\n     * <b>Not intended for use to calculate account balances</b>\n     *\n     * @param account The {@code Account} to calculate the total against\n     * @return total resulting total for this transaction\n     * @see AbstractInvestmentTransactionEntry#getTotal()\n     */\n    public BigDecimal getTotal(final Account account) {\n\n        BigDecimal total = BigDecimal.ZERO;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e instanceof AbstractInvestmentTransactionEntry) {\n                total = total.add(((AbstractInvestmentTransactionEntry) e).getTotal());\n            } else {\n                total = total.add(e.getAmount(account));\n            }\n        }\n\n        return total;\n    }\n\n    /**\n     * Calculates the total of the value of the shares, gains, fees, etc. as it\n     * pertains to an account, but leaves out any cash transfer.\n     * <p>\n     * @param account The {@code Account} to calculate the total against\n     * @return total resulting total for this transaction\n     * @see #getTotal(Account)\n     */\n    BigDecimal getTotalWithoutCashTransfer(final Account account) {\n\n        BigDecimal total = BigDecimal.ZERO;\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e.getTransactionTag() != TransactionTag.INVESTMENT_CASH_TRANSFER) {\n                if (e instanceof AbstractInvestmentTransactionEntry) {\n                    total = total.add(((AbstractInvestmentTransactionEntry) e).getTotal());\n                } else {\n                    total = total.add(e.getAmount(account));\n                }\n            }\n        }\n\n        return total;\n    }\n    \n    /**\n     * Calculates the total cash value of the transaction.\n     * <p>\n     * <b>Not intended for use to calculate account balances</b>\n     *\n     * @return the total cash value of the transaction\n     */\n    public BigDecimal getNetCashValue() {\n        BigDecimal total = getQuantity().multiply(getPrice());\n\n        switch (getTransactionType()) {\n            case DIVIDEND:\n            case RETURNOFCAPITAL:\n                return getAmount(getInvestmentAccount());\n            case REINVESTDIV:\n            case SELLSHARE:\n                total = total.subtract(getFees());\n                break;\n            case BUYSHARE:\n                total = total.add(getFees());\n                break;\n            default:\n                break;\n        }\n\n        return total;\n    }\n\n    /**\n     * Compares two Transactions for ordering. Equality is checked for at the\n     * reference level. If a comparison cannot be determined, the hashCode is\n     * used\n     *\n     * @param tran the {@code Transaction} to be compared.\n     * @return the value {@code 0} if the argument Transaction is equal to\n     * this Transaction; a value less than {@code 0} if this Transaction is\n     * before the Transaction argument; and a value greater than {@code 0}\n     * if this Transaction is after the Transaction argument.\n     */\n    @Override\n    public int compareTo(@NotNull final Transaction tran) {\n        if (tran == this) {\n            return 0;\n        }\n\n        int result = date.compareTo(tran.date);\n        if (result != 0) {\n            return result;\n        }\n\n        result = getTransactionType().name().compareTo(tran.getTransactionType().name());\n        if (result != 0) {\n            return result;\n        }\n\n        result = getMemo().compareTo(tran.getMemo());\n        if (result != 0) {\n            return result;\n        }\n\n        if (tran instanceof InvestmentTransaction) {\n            result = getSecurityNode().compareTo(((InvestmentTransaction) tran).getSecurityNode());\n            if (result != 0) {\n                return result;\n            }\n        }\n\n        result = Long.compareUnsigned(timestamp, tran.timestamp);\n        if (result != 0) {\n            return result;\n        }\n\n        return getUuid().compareTo(tran.getUuid());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/MathConstants.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.MathContext;\nimport java.math.RoundingMode;\n\n/**\n * Math constants utility class.\n *\n * @author Craig Cavanaugh\n */\npublic final class MathConstants {\n\n    /**\n     * Default rounding mode.\n     */\n    public static final RoundingMode roundingMode = RoundingMode.HALF_UP;\n\n    /**\n     * Default math context.\n     */\n    public static final MathContext mathContext = new MathContext(16, roundingMode);\n\n    /**\n     * Default math context for budget values.  Reduces precision to decrease file size.\n     */\n    public static final MathContext budgetMathContext = new MathContext(8, roundingMode);\n\n    /**\n     * Number of significant digits to the right of the decimal SEPARATOR.\n     */\n    public static final int EXCHANGE_RATE_ACCURACY = 6;\n\n    /**\n     * Number of significant digits to the right of the decimal SEPARATOR.\n     */\n    public static final int SECURITY_PRICE_ACCURACY = 6;\n\n    /**\n     * Number of significant digits to the right of the decimal SEPARATOR.\n     */\n    public static final int SECURITY_QUANTITY_ACCURACY = 6;\n\n    public static final int DEFAULT_COMMODITY_PRECISION = 4;\n\n    private MathConstants() {\n        // restrict instantiation\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/QuoteSource.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.lang.reflect.InvocationTargetException;\n\nimport jgnash.net.security.NullParser;\nimport jgnash.net.security.SecurityParser;\nimport jgnash.net.security.YahooEventParser;\nimport jgnash.net.security.iex.IEXParser;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.LogUtil;\nimport jgnash.util.Nullable;\n\n/**\n * Enumeration for quote download source.\n *\n * Not actually used at this time\n *\n * @author Craig Cavanaugh\n */\npublic enum QuoteSource {\n\n    NONE(ResourceUtils.getString(\"QuoteSource.None\"), NullParser.class),\n    YAHOO(ResourceUtils.getString(\"QuoteSource.Yahoo\"), YahooEventParser.class),\n    YAHOO_UK(ResourceUtils.getString(\"QuoteSource.YahooUK\"), YahooEventParser.class),\n    YAHOO_AUS(ResourceUtils.getString(\"QuoteSource.YahooAus\"), YahooEventParser.class),\n    IEX_CLOUD(\"IEX Cloud\", IEXParser.class);\n\n    private final transient String description;\n\n    private final transient Class<? extends SecurityParser> parser;\n\n    /**\n     * Cache the SecurityParser instance to improve performance\n     */\n    private transient SecurityParser securityParser;\n\n    QuoteSource(String description, final Class<? extends SecurityParser> parser) {\n        this.description = description;\n        this.parser = parser;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n    /**\n     * Return a new SecurityParser instance appropriate for the QuoteSource.\n     *\n     * @return a new SecurityParser instance.  Null if not able to create it\n     */\n    @Nullable\n    public SecurityParser getParser() {\n        if (securityParser == null) {\n            try {\n                securityParser = parser.getDeclaredConstructor().newInstance();\n            } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {\n                LogUtil.logSevere(QuoteSource.class, e);\n                return null; // unable to create object\n            }\n        }\n        return securityParser;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/RecTransaction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.util.NotNull;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Objects;\n\n/**\n * Utility Decorator around a Transaction to maintain the original reconciledState state. This class is not intended\n * to be serialized.\n *\n * @author Craig Cavanaugh\n */\npublic class RecTransaction implements Comparable<RecTransaction> {\n    private ReconciledState reconciledState;\n\n    private final Transaction transaction;\n\n    public RecTransaction(@NotNull final Transaction transaction, @NotNull final ReconciledState reconciledState) {\n        Objects.requireNonNull(transaction);\n        Objects.requireNonNull(reconciledState);\n\n        this.transaction = transaction;\n        this.reconciledState = reconciledState;\n    }\n\n    public LocalDate getDate() {\n        return getTransaction().getLocalDate();\n    }\n\n    public String getNumber() {\n        return getTransaction().getNumber();\n    }\n\n    public String getPayee() {\n        return getTransaction().getPayee();\n    }\n\n    public ReconciledState getReconciledState() {\n        return reconciledState;\n    }\n\n    public void setReconciledState(final ReconciledState reconciledState) {\n        this.reconciledState = reconciledState;\n    }\n\n    public BigDecimal getAmount(final Account a) {\n        if (getTransaction() instanceof InvestmentTransaction && a.memberOf(AccountGroup.INVEST)) {\n            return ((InvestmentTransaction) getTransaction()).getMarketValue(getTransaction().getLocalDate())\n                    .add(getTransaction().getAmount(a));\n        }\n\n        return getTransaction().getAmount(a);\n    }\n\n    @Override\n    public int hashCode() {\n        return getTransaction().hashCode();\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        boolean result = false;\n\n        if (obj instanceof RecTransaction) {\n            final RecTransaction other = (RecTransaction) obj;\n\n            if (getTransaction().equals(other.getTransaction()) && reconciledState == other.reconciledState) {\n                result = true;\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public int compareTo(@NotNull final RecTransaction t) {\n        return getTransaction().compareTo(t.getTransaction());\n    }\n\n    public Transaction getTransaction() {\n        return transaction;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/ReconcileManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Manages the reconciliation options and provides utility functions for preserving account reconciliation attributes.\n * <p>\n * Both reconcileIncomeExpense and reconcileBothSides can be false, but only one can be true.\n *\n * @author Craig Cavanaugh\n */\npublic class ReconcileManager {\n    private static final String RECONCILE_INCOME_EXPENSE = \"reconcileIncomeExpense\";\n\n    private static final String RECONCILE_BOTH_SIDES = \"reconcileBothSides\";\n\n    private static boolean reconcileIncomeExpense;\n\n    private static boolean reconcileBothSides;\n\n    private ReconcileManager() {\n    }\n\n    static {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            reconcileIncomeExpense = engine.getBoolean(RECONCILE_INCOME_EXPENSE, false);\n            reconcileBothSides = engine.getBoolean(RECONCILE_BOTH_SIDES, true);\n        }\n    }\n\n    /**\n     * Set so the income or expense side of a transaction is\n     * automatically reconciled.\n     *\n     * @param reconcile true if income and expense accounts should be\n     *                  automatically reconciled\n     */\n    public static void setAutoReconcileIncomeExpense(final boolean reconcile) {\n        reconcileIncomeExpense = reconcile;\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        if (engine != null) {\n            engine.putBoolean(RECONCILE_INCOME_EXPENSE, reconcileIncomeExpense);\n        }\n\n        if (reconcile) {\n            setAutoReconcileBothSides(false);\n        }\n    }\n\n    /**\n     * Determines if the income or expense side of a transaction should\n     * be automatically reconciled.\n     *\n     * @return true if income and expense accounts should automatically be\n     * reconciled.\n     */\n    public static boolean getAutoReconcileIncomeExpense() {\n        return reconcileIncomeExpense;\n    }\n\n    /**\n     * Set so both sides of a double entry transaction are automatically\n     * reconciled.\n     *\n     * @param reconcile true if income and expense accounts should be\n     *                  automatically reconciled\n     */\n    public static void setAutoReconcileBothSides(final boolean reconcile) {\n        reconcileBothSides = reconcile;\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        if (engine != null) {\n            engine.putBoolean(RECONCILE_BOTH_SIDES, reconcileBothSides);\n        }\n\n        if (reconcile) {\n            setAutoReconcileIncomeExpense(false);\n        }\n    }\n\n    /**\n     * Determines if both sides of a transaction should be automatically reconciled.\n     *\n     * @return true if income and expense accounts should automatically be\n     * reconciled.\n     */\n    public static boolean getAutoReconcileBothSides() {\n        return reconcileBothSides;\n    }\n\n    public static boolean isAutoReconcileDisabled() {\n        return !reconcileBothSides && !reconcileIncomeExpense;\n    }\n\n    /**\n     * Disables auto reconciliation.\n     */\n    public static void setDoNotAutoReconcile() {\n        setAutoReconcileBothSides(false);\n        setAutoReconcileIncomeExpense(false);\n    }\n\n    /**\n     * Sets the reconciled state of the transaction using the rules set\n     * by the user.\n     *\n     * @param account    Base account\n     * @param t          Transaction to reconcile\n     * @param reconciled Reconciled state\n     */\n    public static void reconcileTransaction(final Account account, final Transaction t, final ReconciledState reconciled) {\n\n        // mark transaction reconciled for the primary account\n        t.setReconciled(account, reconciled);\n\n        if (getAutoReconcileBothSides()) {\n            t.setReconciled(reconciled);\n        } else if (getAutoReconcileIncomeExpense()) {\n            List<TransactionEntry> entries = t.getTransactionEntries();\n\n            for (TransactionEntry entry : entries) {\n\n                Account c = entry.getCreditAccount();\n                if (c.instanceOf(AccountType.INCOME) || c.instanceOf(AccountType.EXPENSE)) {\n                    entry.setCreditReconciled(ReconciledState.RECONCILED);\n                }\n\n                Account d = entry.getDebitAccount();\n                if (d.instanceOf(AccountType.INCOME) || d.instanceOf(AccountType.EXPENSE)) {\n                    entry.setDebitReconciled(ReconciledState.RECONCILED);\n                }\n            }\n        }\n    }\n\n    public static void reconcileTransactions(final Account account, final List<RecTransaction> list,\n                                             final ReconciledState reconciledState) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        // create a copy of the list to prevent concurrent modification errors\n        final List<RecTransaction> transactions = new ArrayList<>(list);\n\n        // Set to the requested reconcile state, ignore if no change is detected\n        transactions.parallelStream().filter(transaction -> transaction.getReconciledState()\n                != transaction.getTransaction().getReconciled(account)).forEach(recTransaction -> {\n\n            // Set to the requested reconcile state\n            if (recTransaction.getReconciledState() != ReconciledState.NOT_RECONCILED) {\n                engine.setTransactionReconciled(recTransaction.getTransaction(), account, reconciledState);\n            } else { // must have been reconciled or cleared\n                engine.setTransactionReconciled(recTransaction.getTransaction(), account, recTransaction.getReconciledState());\n            }\n        });\n    }\n\n    /**\n     * Sets a date attribute for an account.\n     *\n     * @param account account that is being updated\n     * @param attribute key for the date attribute\n     * @param date date value\n     */\n    public static void setAccountDateAttribute(@NotNull final Account account, @NotNull final String attribute,\n                                               @NotNull final LocalDate date) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        engine.setAccountAttribute(account, attribute, Long.toString(DateUtils.asEpochMilli(date)));\n    }\n\n    /**\n     * Returns a date attribute for an account.\n     *\n     * @param account {@code Account} that is being queried\n     * @param attribute key for the date attribute\n     * @return an {@code Optional} containing the date if previously set\n     */\n    public static Optional<LocalDate> getAccountDateAttribute(@NotNull final Account account,\n                                                              @NotNull final String attribute) {\n        final String value = account.getAttribute(attribute);\n        if (value != null) {\n            return Optional.ofNullable(DateUtils.asLocalDate(Long.parseLong(value)));\n        }\n        return Optional.empty();\n    }\n\n    /**\n     * Sets a {@code BigDecimal} attribute for an account.\n     *\n     * @param account account that is being updated\n     * @param attribute key for the {@code BigDecimal} attribute\n     * @param decimal decimal value\n     */\n    public static void setAccountBigDecimalAttribute(@NotNull final Account account, @NotNull final String attribute,\n                                                     @NotNull final BigDecimal decimal) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        engine.setAccountAttribute(account, attribute, decimal.toString());\n    }\n\n    /**\n     * Returns a {@code BigDecimal} attribute for an account.\n     *\n     * @param account {@code Account} that is being queried\n     * @param attribute key for the {@code BigDecimal} attribute\n     * @return an {@code Optional} containing the {@code BigDecimal} if previously set\n     */\n    public static Optional<BigDecimal> getAccountBigDecimalAttribute(@NotNull final Account account,\n                                                                     @NotNull final String attribute) {\n        final String value = account.getAttribute(attribute);\n        if (value != null) {\n            return Optional.of(new BigDecimal(value));\n        }\n        return Optional.empty();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/ReconciledState.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Enumeration for the reconciled state of a transaction.\n *\n * @author Craig Cavanaugh\n */\npublic enum ReconciledState {\n    CLEARED(ResourceUtils.getString(\"State.Cleared\")),\n    NOT_RECONCILED(ResourceUtils.getString(\"State.NotReconciled\")),\n    RECONCILED(ResourceUtils.getString(\"State.Reconciled\"));\n\n    private final transient String symbol;\n\n    ReconciledState(final String symbol) {\n        this.symbol = symbol;\n    }\n\n    @Override\n    public String toString() {\n        return symbol;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/RootAccount.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport javax.persistence.Entity;\n\n/**\n * The RootAccount class.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class RootAccount extends Account {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    public RootAccount() {\n        super();\n    }\n\n    /**\n     * Public constructor.\n     *\n     * @param node The base commodity for this data set.\n     */\n    protected RootAccount(final CurrencyNode node) {\n        super(AccountType.ROOT, node);\n    }\n\n    @Override\n    public AccountType getAccountType() {\n        return AccountType.ROOT;\n    }\n\n    /**\n     * Override the super.  This account does not have a valid or usable path.\n     *\n     * @return returns an empty string.\n     */\n    @Override\n    public synchronized String getPathName() {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/SecurityHistoryEvent.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.util.NotNull;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.SequenceGenerator;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Objects;\n\n/**\n * Represents security history events such as splits and dividends.\n * <p>\n * Equality is assumed if the date and type match.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class SecurityHistoryEvent implements Comparable<SecurityHistoryEvent>, Serializable {\n\n    @SuppressWarnings(\"unused\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    public long id;\n\n    @Enumerated(EnumType.STRING)\n    private SecurityHistoryEventType type = SecurityHistoryEventType.DIVIDEND;\n\n    @Column(precision = 19, scale = 4)\n    private BigDecimal value = BigDecimal.ZERO;\n\n    private LocalDate date = LocalDate.now();\n\n    /**\n     * Cached hash code\n     */\n    private transient int hash = 0;\n\n    /**\n     * public no-argument constructor for reflection.\n     */\n    @SuppressWarnings(\"unused\")\n    public SecurityHistoryEvent() {\n    }\n\n    public SecurityHistoryEvent(@NotNull final SecurityHistoryEventType type, @NotNull final LocalDate date,\n                                @NotNull final BigDecimal value) {\n        this.type = type;\n        this.date = date;\n        this.value = value;\n    }\n\n    public SecurityHistoryEventType getType() {\n        return type;\n    }\n\n    public BigDecimal getValue() {\n        return value;\n    }\n\n    public LocalDate getDate() {\n        return date;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) {\n            return true;\n        }\n\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        SecurityHistoryEvent event = (SecurityHistoryEvent) o;\n\n        return Objects.equals(type, event.type) && date.compareTo(((SecurityHistoryEvent) o).getDate()) == 0;\n    }\n\n    @Override\n    public int hashCode() {\n        int h = hash;\n\n        if (h == 0) {\n            hash = h = Objects.hash(type, date);\n        }\n        return h;\n    }\n\n    @Override\n    public int compareTo(@NotNull final SecurityHistoryEvent historyEvent) {\n        if (historyEvent == this) {\n            return 0;\n        }\n\n        int result = date.compareTo(historyEvent.getDate());\n        if (result != 0) {\n            return result;\n        }\n\n        return type.compareTo(historyEvent.getType());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/SecurityHistoryEventType.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Historical event descriptor for securities.\n *\n * @author Craig Cavanaugh\n */\npublic enum SecurityHistoryEventType {\n    SPLIT(ResourceUtils.getString(\"SecurityEvent.Split\")),\n    DIVIDEND(ResourceUtils.getString(\"SecurityEvent.Dividend\")),\n    PRICE(\"SecurityEvent.Price\");\n\n    private final transient String description;\n\n    SecurityHistoryEventType(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/SecurityHistoryNode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.SequenceGenerator;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.Objects;\n\n/**\n * Historical data for a {@code SecurityNode}.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class SecurityHistoryNode implements Comparable<SecurityHistoryNode>, Serializable {\n\n    @SuppressWarnings(\"unused\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    public long id;\n\n    private LocalDate date = LocalDate.now();\n\n    @Column(precision = 19, scale = 4)\n    private BigDecimal price = BigDecimal.ZERO;\n\n    @Column(precision = 19, scale = 4)\n    private BigDecimal high = BigDecimal.ZERO;\n\n    @Column(precision = 19, scale = 4)\n    private BigDecimal low = BigDecimal.ZERO;\n\n    private long volume = 0;\n\n    private transient BigDecimal adjustedPrice = null;\n\n    /**\n     * public no-argument constructor for reflection.\n     */\n    public SecurityHistoryNode() {\n    }\n\n    /**\n     * Public constructor for creating a history node.\n     *\n     * @param date   date\n     * @param price  closing price\n     * @param volume closing volume\n     * @param high   high price for the day\n     * @param low    low price for the day\n     */\n    public SecurityHistoryNode(@NotNull final LocalDate date, @Nullable final BigDecimal price, final long volume,\n                               @Nullable final BigDecimal high, @Nullable final BigDecimal low) {\n        setDate(date);\n        setPrice(price);\n        setVolume(volume);\n        setHigh(high);\n        setLow(low);\n    }\n\n    private void setHigh(final BigDecimal high) {\n        if (high != null) {\n            this.high = high;\n        }\n    }\n\n    private void setLow(final BigDecimal low) {\n        if (low != null) {\n            this.low = low;\n        }\n    }\n\n    private void setVolume(final long volume) {\n        this.volume = volume;\n    }\n\n    public BigDecimal getHigh() {\n        return high;\n    }\n\n    public BigDecimal getLow() {\n        return low;\n    }\n\n    public long getVolume() {\n        return volume;\n    }\n\n    void setDate(final @NotNull LocalDate localDate) {\n        Objects.requireNonNull(localDate);\n        this.date = localDate;\n    }\n\n    public LocalDate getLocalDate() {\n        return date;\n    }\n\n    void setPrice(final BigDecimal price) {\n        if (price != null) {\n            this.price = price;\n        }\n    }\n\n    public BigDecimal getPrice() {\n        return price;\n    }\n\n    /**\n     * The price adjusted for any splits or reverse splits.\n     *\n     * @return the adjusted price\n     */\n    public @NotNull\n    BigDecimal getAdjustedPrice() {\n        if (adjustedPrice != null) {\n            return adjustedPrice;\n        }\n        return price;\n    }\n\n    /**\n     * Adjusts the historical values given a multiplier.  To be used for handling\n     * security splits and reverse splits.\n     *\n     * @param multiplier multiplier to be used for adjusting prices\n     */\n    void setAdjustmentMultiplier(@NotNull BigDecimal multiplier) {\n        if (price != null) {\n            adjustedPrice = price.multiply(multiplier, MathConstants.mathContext);\n        }\n    }\n\n    /**\n     * Compare using only the {@code LocalDate}\n     *\n     * @param node node to compare\n     */\n    @Override\n    public int compareTo(@NotNull final SecurityHistoryNode node) {\n        return getLocalDate().compareTo(node.getLocalDate());\n    }\n\n    /**\n     * Equality is based on the {@code LocalDate} of the SecurityHistoryNode.\n     *\n     * @param obj the reference SecurityHistoryNode with which to compare.\n     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.\n     */\n    @Override\n    public boolean equals(final Object obj) {\n        return this == obj || obj instanceof SecurityHistoryNode && date\n                .compareTo(((SecurityHistoryNode) obj).date) == 0;\n    }\n\n    @Override\n    public int hashCode() {\n        return date.hashCode();\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/SecurityNode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.FetchType;\nimport javax.persistence.JoinTable;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.OneToMany;\nimport javax.persistence.PostLoad;\n\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Security Node.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class SecurityNode extends CommodityNode {\n\n    @ManyToOne\n    private CurrencyNode reportedCurrency;\n\n    /**\n     * The currency that security values are reported in.\n     */\n    @Enumerated(EnumType.STRING)\n    private QuoteSource quoteSource = QuoteSource.NONE;\n\n    /**\n     * ISIN or CUSIP,  Used for OFX and quote downloads.\n     */\n    private String isin;\n\n    @JoinTable\n    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)\n    private Set<SecurityHistoryNode> historyNodes = new HashSet<>();\n\n    @JoinTable\n    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)\n    private final Set<SecurityHistoryEvent> securityHistoryEvents = new HashSet<>();\n\n    private transient ReadWriteLock lock;\n\n    private transient List<SecurityHistoryNode> sortedHistoryNodeCache = new ArrayList<>();\n\n    public SecurityNode() {\n        lock = new ReentrantReadWriteLock(true);\n    }\n\n    public SecurityNode(final CurrencyNode node) {\n        this();\n        setReportedCurrencyNode(node);\n    }\n\n    /**\n     * Prefix is deferred to the reported currency.\n     *\n     * @return prefix of the reported currency\n     */\n    @Override\n    public String getPrefix() {\n        return reportedCurrency.getPrefix();\n    }\n\n    @Override\n    public void setPrefix(final String ignored) {\n        // prefix is controlled by the reported currency\n    }\n\n    /**\n     * Suffix is deferred to the reported currency.\n     *\n     * @return suffix of the reported currency\n     */\n    @Override\n    public String getSuffix() {\n        return reportedCurrency.getSuffix();\n    }\n\n    @Override\n    public void setSuffix(final String ignored) {\n        // suffix is controlled by the reported currency\n    }\n\n    /**\n     * Returns the quote download source.\n     *\n     * @return quote download source\n     */\n    public QuoteSource getQuoteSource() {\n        return quoteSource;\n    }\n\n    /**\n     * Sets the quote download source.\n     *\n     * @param source QuoteSource to use\n     */\n    public void setQuoteSource(final QuoteSource source) {\n        quoteSource = source;\n    }\n\n    @NotNull\n    public String getISIN() {\n        return (isin == null) ? \"\" : isin;\n    }\n\n    public void setISIN(final String isin) {\n        this.isin = isin;\n    }\n\n    /**\n     * Set the CurrencyNode that security histories are reported in.\n     *\n     * @param node reported CurrencyNode\n     */\n    public void setReportedCurrencyNode(final CurrencyNode node) {\n        reportedCurrency = node;\n    }\n\n    /**\n     * Returns the CurrencyNode that security histories are reported in.\n     *\n     * @return reported CurrencyNode\n     */\n    public CurrencyNode getReportedCurrencyNode() {\n        return reportedCurrency;\n    }\n\n    boolean addHistoryNode(final SecurityHistoryNode node) {\n\n        lock.writeLock().lock();\n\n        try {\n            sortedHistoryNodeCache.add(node);\n            Collections.sort(sortedHistoryNodeCache);\n\n            return historyNodes.add(node);\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    boolean removeHistoryNode(final LocalDate date) {\n        lock.writeLock().lock();\n\n        try {\n            final boolean result = historyNodes.removeIf(node -> node.getLocalDate().compareTo(date) == 0);\n\n            if (result) {\n                sortedHistoryNodeCache.removeIf(node -> node.getLocalDate().compareTo(date) == 0);\n            }\n\n            return result;\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    boolean addSecurityHistoryEvent(final SecurityHistoryEvent securityHistoryEvent) {\n        lock.writeLock().lock();\n\n        try {\n            return securityHistoryEvents.add(securityHistoryEvent);\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    boolean removeSecurityHistoryEvent(final SecurityHistoryEvent securityHistoryEvent) {\n        boolean result = false;\n\n        lock.writeLock().lock();\n\n        try {\n            // Use equality check for remove\n            for (final SecurityHistoryEvent historyEvent : securityHistoryEvents) {\n                if (historyEvent.equals(securityHistoryEvent)) {\n                    result = securityHistoryEvents.remove(historyEvent);\n                    break;  // break to prevent concurrent modification error\n                }\n            }\n        } finally {\n            lock.writeLock().unlock();\n        }\n\n        return result;\n    }\n\n\n    /**\n     * Returns <tt>true</tt> if this SecurityNode contains the specified element.\n     *\n     * @param date LocalDate whose presence in this SecurityNode is to be tested\n     * @return <tt>true</tt> if this SecurityNode contains a SecurityHistoryNode with the specified date\n     */\n    public boolean contains(final LocalDate date) {\n        boolean result = false;\n\n        lock.readLock().lock();\n\n        try {\n            for (final SecurityHistoryNode node : historyNodes) {\n                if (node.getLocalDate().compareTo(date) == 0) {\n                    result = true;\n                    break;\n                }\n            }\n        } finally {\n            lock.readLock().unlock();\n        }\n\n        return result;\n    }\n\n    /**\n     * Gets the SecurityHistoryNodes for this security.  At time of retrieval the adjusted price of the\n     * SecurityHistoryNodes will be updated to reflect any spits or reverse splits\n     *\n     * @return Returns a shallow copy of the history nodes to protect against modification\n     * @see SecurityHistoryNode#getAdjustedPrice()\n     */\n    public List<SecurityHistoryNode> getHistoryNodes() {\n\n        lock.readLock().lock();\n\n        try {\n            final List<SecurityHistoryEvent> splits = getSplitEvents();\n\n            if (!splits.isEmpty()) {\n                BigDecimal scalar = BigDecimal.ONE;\n\n                final ListIterator<SecurityHistoryEvent> historyEventIterator = splits.listIterator(splits.size());\n\n                LocalDate eventDate = historyEventIterator.previous().getDate();\n                historyEventIterator.next();    // reset back to the tail\n\n                // work backwards\n                for (int i = sortedHistoryNodeCache.size() - 1; i >= 0; i--) {\n                    if (DateUtils.after(eventDate, sortedHistoryNodeCache.get(i).getLocalDate())\n                            && historyEventIterator.hasPrevious()) {\n                        final SecurityHistoryEvent historyEvent = historyEventIterator.previous();\n                        eventDate = historyEvent.getDate();\n                        scalar = scalar.divide(historyEvent.getValue(), MathConstants.mathContext);\n                    }\n\n                    sortedHistoryNodeCache.get(i).setAdjustmentMultiplier(scalar);\n                }\n            }\n\n            return Collections.unmodifiableList(sortedHistoryNodeCache);\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Convenience function to return the upper and lower date bounds.\n     *\n     * @return an array of LocalDate with {@code [0]} being the lower bound and {@code [1]} being the upper bound\n     */\n    public Optional<LocalDate[]> getLocalDateBounds() {\n        lock.readLock().lock();\n\n        try {\n            if (sortedHistoryNodeCache.size() > 1) {\n                return Optional.of(new LocalDate[]{\n                        sortedHistoryNodeCache.get(0).getLocalDate(),\n                        sortedHistoryNodeCache.get(sortedHistoryNodeCache.size() - 1).getLocalDate()\n                });\n            }\n            return Optional.empty();\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the history node events split into groups by historical splits or reverse splits.\n     *\n     * @return a List of Lists of SecurityHistoryNodes\n     */\n    public List<List<SecurityHistoryNode>> getHistoryNodeGroupsBySplits() {\n        lock.readLock().lock();\n\n        try {\n\n            final List<List<SecurityHistoryNode>> groups = new ArrayList<>();\n            final List<SecurityHistoryEvent> splitEvents = getSplitEvents();\n\n            if (splitEvents.isEmpty()) {\n                groups.add(getHistoryNodes());\n            } else {    // count should be split events + 1 when complete\n\n                // Create a defensive copy that has the adjustment multiplier set\n                final List<SecurityHistoryNode> securityHistoryNodes = getHistoryNodes();\n                final ListIterator<SecurityHistoryEvent> historyEventIterator = splitEvents.listIterator();\n\n                LocalDate eventDate = historyEventIterator.next().getDate();\n\n                List<SecurityHistoryNode> group = new ArrayList<>();\n\n                for (int i = 0; i < securityHistoryNodes.size(); i++) {\n                    if (eventDate == null || securityHistoryNodes.get(i).getLocalDate().isBefore(eventDate)) {\n                        group.add(securityHistoryNodes.get(i));\n                    } else {\n                        groups.add(group);                              // save the current group\n                        group = new ArrayList<>();                      // start a new group\n                        group.add(securityHistoryNodes.get(i - 1));      // create continuity with the previous group\n                        group.add(securityHistoryNodes.get(i));          // add the current node\n\n                        if (historyEventIterator.hasNext()) {\n                            eventDate = historyEventIterator.next().getDate();\n                        } else {\n                            eventDate = null;\n                        }\n                    }\n                }\n                groups.add(group);  // add last group\n            }\n\n            return groups;\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Get an unmodifiable copy of the SecurityHistoryEvents for this security.\n     *\n     * @return returns a shallow copy of the SecurityHistoryEvents to protect against modification\n     */\n    public Set<SecurityHistoryEvent> getHistoryEvents() {\n        return Collections.unmodifiableSet(securityHistoryEvents);\n    }\n\n    private List<SecurityHistoryEvent> getSplitEvents() {\n        lock.readLock().lock();\n\n        try {\n            return securityHistoryEvents.stream().filter(historyEvent\n                    -> historyEvent.getType() == SecurityHistoryEventType.SPLIT).sorted().collect(Collectors.toList());\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the {@code SecurityHistoryNode} with the matching date.\n     *\n     * @param date Date to match\n     * @return {@code Optional} contain a matching node\n     */\n    public Optional<SecurityHistoryNode> getHistoryNode(final LocalDate date) {\n        lock.readLock().lock();\n\n        try {\n            SecurityHistoryNode hNode = null;\n\n            // Work backwards through the list as the newest date is requested the most\n            for (int i = sortedHistoryNodeCache.size() - 1; i >= 0; i--) {\n                final SecurityHistoryNode node = sortedHistoryNodeCache.get(i);\n\n                if (date.compareTo(node.getLocalDate()) == 0) {\n                    hNode = node;\n                    break;\n                }\n            }\n\n            return Optional.ofNullable(hNode);\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns the {@code SecurityHistoryNode} with the closet matching date without exceeding the request date.\n     *\n     * @param date {@code Date} to match\n     * @return {@code Optional} containing a {@code SecurityHistoryNode} if a match is found\n     */\n    public Optional<SecurityHistoryNode> getClosestHistoryNode(final LocalDate date) {\n        final long epochDay = date.toEpochDay();\n\n        lock.readLock().lock();\n\n        try {\n            SecurityHistoryNode hNode = null;\n\n            // Work backwards through the list as the newest date is requested the most\n            for (int i = sortedHistoryNodeCache.size() - 1; i >= 0; i--) {\n                final SecurityHistoryNode node = sortedHistoryNodeCache.get(i);\n\n                if (node.getLocalDate().toEpochDay() <= epochDay) {\n                    hNode = node;\n                    break;\n                }\n            }\n\n            return Optional.ofNullable(hNode);\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    private BigDecimal getMarketPrice(final LocalDate date) {\n        BigDecimal marketPrice = BigDecimal.ZERO;\n\n        final Optional<SecurityHistoryNode> optional = getClosestHistoryNode(date);\n\n        if (optional.isPresent()) {\n            marketPrice = optional.get().getPrice();\n        }\n\n        return marketPrice;\n    }\n\n    /**\n     * Returns the latest market price exchanged to the specified currency.\n     *\n     * @param date date to find closest matching rate without exceeding\n     * @param node currency to exchange to\n     * @return latest market price\n     */\n    public BigDecimal getMarketPrice(final LocalDate date, final CurrencyNode node) {\n        return getMarketPrice(date).multiply(getReportedCurrencyNode().getExchangeRate(node));\n    }\n\n    /**\n     * Return a clone of this security node.  Security history is not cloned\n     *\n     * @return clone of this SecurityNode with history nodes\n     */\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n\n        lock.readLock().lock();\n\n        try {\n            SecurityNode node = (SecurityNode) super.clone();\n            node.historyNodes = new HashSet<>();\n            node.postLoad();\n\n            return node;\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * .\n     * Required by XStream for proper initialization\n     *\n     * @return Properly initialized SecurityNode\n     */\n    protected Object readResolve() {\n        postLoad();\n        return this;\n    }\n\n    @PostLoad\n    private void postLoad() {\n        lock = new ReentrantReadWriteLock(true);\n\n        // load the cache list\n        sortedHistoryNodeCache = new ArrayList<>(historyNodes);\n        Collections.sort(sortedHistoryNodeCache);   // JPA will be naturally sorted, but XML files will not\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/StoredObject.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.Serializable;\nimport java.util.UUID;\n\nimport javax.persistence.Basic;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Inheritance;\nimport javax.persistence.InheritanceType;\nimport javax.persistence.Version;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Abstract class for anything stored in the database that requires a unique id.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@Inheritance(strategy = InheritanceType.JOINED)\npublic abstract class StoredObject implements Cloneable, Serializable {\n\n    /**\n     * Version field for persistence purposes.\n     */\n    @Version\n    @SuppressWarnings(\"unused\")\n    private int version;\n\n    /**\n     * Indicates object is marked for removal.\n     */\n    @Basic\n    private boolean markedForRemoval = false;\n\n    /**\n     * String based Unique ID for every object.\n     */\n    @Id\n    @Column(name = \"uuid\", nullable = false, updatable = false)\n    private UUID uuid = UUID.randomUUID();\n\n    /** Cache the hash code for the StoredObject */\n    private transient int hash = 0;\n\n    /**\n     * Getter for the uuid.\n     *\n     * Note: method should not be final (HHH000305)\n     *\n     * @return uuid of the object\n     */\n    public UUID getUuid() {\n        return uuid;\n    }\n\n    /**\n     * Setter for the uuid.  Used for reflection purposes only\n     *\n     * @param uuid uuid to assign the object\n     */\n    private void setUuid(final UUID uuid) {\n        this.uuid = uuid;\n    }\n\n    void setMarkedForRemoval() {\n        this.markedForRemoval = true;\n    }\n\n    public boolean isMarkedForRemoval() {\n        return markedForRemoval;\n    }\n\n    /**\n     * Override hashCode to use UUID hash code.\n     *\n     * The hash is cached since UUID does not cache\n     *\n     * @see java.util.UUID#hashCode()\n     */\n    @Override\n    public int hashCode() {\n        int h = hash;\n\n        if (h == 0) {\n            hash = h = getUuid().hashCode();\n        }\n\n        return h;\n    }\n\n    /**\n     * Default equals override.\n     *\n     * @param o object to compare\n     * @see java.lang.Object#equals(java.lang.Object)\n     */\n    @Override\n    public boolean equals(final Object o) {\n        return this == o || o instanceof StoredObject && getUuid().equals(((StoredObject) o).getUuid());\n\n    }\n\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n        StoredObject o = null;\n\n        try {\n            o = (StoredObject) super.clone();\n            o.setUuid(UUID.randomUUID());   // force a new UUID\n            o.hash = 0;\n            o.markedForRemoval = false;\n        } catch (final CloneNotSupportedException e) {\n            logSevere(StoredObject.class, e);\n        }\n\n        return o;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/StoredObjectComparator.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.Serializable;\nimport java.util.Comparator;\n\n/**\n * High level StoredObject comparator.  When serializing to an XML file, this helps with readability of references\n *\n * @author Craig Cavanaugh\n */\npublic class StoredObjectComparator implements Comparator<StoredObject>, Serializable {\n\n    @Override\n    public int compare(final StoredObject o1, final StoredObject o2) {\n\n        if ((o1 instanceof CurrencyNode || o2 instanceof CurrencyNode) && !(o1 instanceof CurrencyNode && o2 instanceof CurrencyNode)) {\n            if (o1 instanceof CurrencyNode) {\n                return -1;\n            }\n            return 1;\n        }\n\n        if ((o1 instanceof SecurityNode || o2 instanceof SecurityNode) && !(o1 instanceof SecurityNode && o2 instanceof SecurityNode)) {\n\n            if (o1 instanceof SecurityNode) {\n                return -1;\n            }\n            return 1;\n        }\n\n        if (o1 instanceof Config && o2 instanceof RootAccount) {\n            return -1;\n        }\n\n        if (o1 instanceof RootAccount && o2 instanceof Config) {\n            return 1;\n        }\n\n        // two config objects should never occur\n        if ((o1 instanceof Config || o2 instanceof Config) && !(o1 instanceof Config && o2 instanceof Config)) {\n            if (o1 instanceof Config) {\n                return -1;\n            }\n            return 1;\n        }\n\n        // two root accounts should never occur\n        if ((o1 instanceof RootAccount || o2 instanceof RootAccount) && !(o1 instanceof RootAccount && o2 instanceof RootAccount)) {\n            if (o1 instanceof RootAccount) {\n                return -1;\n            }\n            return 1;\n        }\n\n        if (!o1.getClass().equals(o2.getClass())) {\n            return o1.getClass().getName().compareTo(o2.getClass().getName());\n        }\n\n        return o1.getUuid().compareTo(o2.getUuid());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/Tag.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\n\n/**\n * Class for user created tags to mark transactions.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class Tag extends StoredObject implements Comparable<Tag> {\n\n    /**\n     * Tag name\n     */\n    private String name = \"\";\n\n    /**\n     * Tag description\n     */\n    @Column(columnDefinition = \"VARCHAR(2048)\")\n    private String description = \"\";\n\n    /**\n     * Tag color\n     * <p>\n     * Default value is black\n     */\n    private long color = 255;\n\n    /**\n     * Icon unicode value\n     */\n    private Integer unicode = 0xf764;   // circle\n\n    public Tag() {\n        // zero arg constructor required for persistence\n    }\n\n    public void setDescription(final String description) {\n        if (description != null && description.length() <= 2048) {\n            this.description = description;\n        }\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setName(final String name) {\n        if (name != null && !name.isBlank()) {\n            this.name = name;\n        }\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    /**\n     * Sets the Tag color\n     *\n     * @see jgnash.util.EncodeDecode#longToColorString(long)\n     * @param color integer equivalent of a web based color string\n     */\n    public void setColor(final long color) {\n        this.color = color;\n    }\n\n    /**\n     * Returns the Tag color\n     *\n     * @see jgnash.util.EncodeDecode#colorStringToLong(String)\n     * @return integer value encoded from a web based color string\n     */\n    public long getColor() {\n        return color;\n    }\n\n    /**\n     * Returns the Tag shape\n     *\n     * @return char value of the font character\n     */\n    public int getShape() {\n        if (unicode == null) {\n            unicode = 0xf0e4; // bug symbol.. from conversion of an older file\n        }\n        return unicode;\n    }\n\n    /**\n     * Sets the Tag shape\n     *\n     * @param unicode value of the font character\n     */\n    public void setShape(final int unicode) {\n        this.unicode = unicode;\n    }\n\n    @Override\n    public java.lang.String toString() {\n        return getName();\n    }\n\n    /**\n     * Compares this object with the specified object for order.  Returns a\n     * negative integer, zero, or a positive integer as this object is less\n     * than, equal to, or greater than the specified object.\n     *\n     * @param o the object to be compared.\n     * @return a negative integer, zero, or a positive integer as this object\n     * is less than, equal to, or greater than the specified object.\n     * @throws NullPointerException if the specified object is null\n     * @throws ClassCastException   if the specified object's type prevents it\n     *                              from being compared to this object.\n     */\n    @Override\n    public int compareTo(final Tag o) {\n        if (o == this) {\n            return 0;\n        }\n\n        int result = name.compareTo(o.name);\n        if (result != 0) {\n            return result;\n        }\n\n        result = Long.compare(color, o.color);\n        if (result != 0) {\n            return result;\n        }\n\n        result = Integer.compare(getShape(), o.getShape());\n        if (result != 0) {\n            return result;\n        }\n\n        result = description.compareTo(o.description);\n        if (result != 0) {\n            return result;\n        }\n\n        return getUuid().compareTo(o.getUuid());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/Transaction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.Basic;\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.FetchType;\nimport javax.persistence.JoinTable;\nimport javax.persistence.OneToMany;\nimport javax.persistence.Table;\n\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Base class for transactions.  Transaction should be treated as immutable as in not modified if they have\n * been persisted within the database.\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"JpaDataSourceORMInspection\")\n@Entity\n@Table(name = \"TRANSACT\") // cannot use \"Transaction\" as the table name or it causes an SQL error!!!!\npublic class Transaction extends StoredObject implements Comparable<Transaction> {\n\n    private static final transient String EMPTY = \"\";\n\n    /**\n     * If the memo consists of only the summation symbol, memos from the TransactionEntries are concatenated.\n     */\n    public static final transient String CONCATENATE = \"Ʃ\";\n\n    /**\n     * Date of entry from form entry, used for sort order.\n     */\n    LocalDate date = LocalDate.now();\n\n    /**\n     * Timestamp for transaction creation.\n     */\n    @Column(name = \"timestamp\", nullable = false, columnDefinition = \"BIGINT default 0\")\n    long timestamp = System.currentTimeMillis();\n\n    /**\n     * Transaction number.\n     */\n    private String number;\n\n    /**\n     * Transaction payee.\n     */\n    private String payee;\n\n    /**\n     * Financial Institute Transaction ID. Typically used for OFX import FITID.  If this field is not null\n     * then it is an indicator of an imported transaction\n     */\n    @Basic\n    private String fitid;\n\n    /**\n     * File name for the attachment, should not contain any preceding paths.\n     */\n    @Column(columnDefinition = \"VARCHAR(256)\")\n    private String attachment;\n\n    /**\n     * Transaction memo.\n     */\n    @Column(columnDefinition = \"VARCHAR(1024)\")\n    private String memo;\n\n    /**\n     * Cache the concatenated memo\n     */\n    private transient String concatMemo;\n\n    /**\n     * Cache the generated LocalDateTime\n     */\n    private transient LocalDateTime timeStampDate;\n\n    /**\n     * Transaction entries.\n     */\n    @JoinTable\n    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)\n    Set<TransactionEntry> transactionEntries = new HashSet<>();\n\n    /**\n     * Public constructor.\n     */\n    public Transaction() {\n        // zero arg constructor required for persistence\n    }\n\n    /**\n     * Returns a set of accounts this transaction effects.\n     * The returned set may be altered without creating side effects\n     *\n     * @return set of accounts\n     * @see Account\n     */\n    @NotNull\n    public Set<Account> getAccounts() {\n        final Set<Account> accounts = new TreeSet<>();\n\n        for (final TransactionEntry e : transactionEntries) {\n            accounts.add(e.getCreditAccount());\n            accounts.add(e.getDebitAccount());\n        }\n\n        return accounts;\n    }\n\n    /**\n     * Determines if any of the transaction's accounts are hidden.\n     *\n     * @return true if any of the accounts are hidden\n     */\n    public boolean areAccountsHidden() {\n        boolean accountsHidden = false;\n\n        for (final Account account : getAccounts()) {\n            if (!account.isVisible()) {\n                accountsHidden = true;\n                break;\n            }\n        }\n\n        return accountsHidden;\n    }\n\n    /**\n     * Determines if any of the transaction's accounts are locked against editing (cloning and then changing accounts).\n     *\n     * @return true if any of the accounts are locked\n     */\n    public boolean areAccountsLocked() {\n        boolean accountsLocked = false;\n\n        for (final Account account : getAccounts()) {\n            if (account.isLocked()) {\n                accountsLocked = true;\n                break;\n            }\n        }\n\n        return accountsLocked;\n    }\n\n    /**\n     * Search for a common account for all entries.\n     *\n     * @return the common Account\n     * @see Account\n     */\n    public Account getCommonAccount() {\n        Account account = null;\n\n        if (size() >= 2) {\n            for (final Account a : getAccounts()) {\n                boolean success = true;\n                for (final TransactionEntry e : transactionEntries) {\n                    if (!e.getCreditAccount().equals(a) && !e.getDebitAccount().equals(a)) {\n                        success = false;\n                        break;\n                    }\n                }\n                if (success) {\n                    account = a;\n                    break;\n                }\n            }\n        } else { // double entry transaction, return the credit account by default\n            account = transactionEntries.iterator().next().getCreditAccount();\n        }\n\n        return account;\n    }\n\n    /**\n     * Returns the balance of the transaction amount with respect to the supplied account. Value will be positive or\n     * negative depending if the transaction debits or credits the account.\n     *\n     * @param entry new TransactionEntry to add\n     * @see TransactionEntry\n     */\n    public void addTransactionEntry(@NotNull final TransactionEntry entry) {\n        Objects.requireNonNull(entry);\n\n        if (transactionEntries.contains(entry)) {\n            throw new IllegalArgumentException(\"Duplicate entry is not allowed\");\n        }\n\n        transactionEntries.add(entry);\n    }\n\n    public void removeTransactionEntry(@NotNull final TransactionEntry entry) {\n        Objects.requireNonNull(entry);\n\n        transactionEntries.remove(entry);\n    }\n\n    /**\n     * Returns the number of {@code TransactionEntry(s)} this transaction contains. A read lock is obtained before\n     * determining the size.\n     *\n     * @return the number of {@code TransactionEntry(s)}\n     * @see TransactionEntry\n     */\n    public int size() {\n        return transactionEntries.size();\n    }\n\n    public void setDate(@NotNull final LocalDate localDate) {\n        this.date = localDate;\n    }\n\n    public LocalDate getLocalDate() {\n        return date;\n    }\n\n    /**\n     * Sets the payee for this transaction.\n     *\n     * @param payee the transaction payee\n     */\n    public void setPayee(@Nullable final String payee) {\n        this.payee = payee;\n    }\n\n    /**\n     * Return the payee for this transaction.\n     *\n     * @return the transaction payee. Guaranteed to not return null\n     */\n    @NotNull\n    public String getPayee() {\n        String result = EMPTY;\n\n        if (payee != null) {\n            result = payee;\n        }\n        return result;\n    }\n\n    /**\n     * Sets the number for this transaction.\n     *\n     * @param number the transaction number\n     */\n    public void setNumber(@Nullable final String number) {\n        this.number = number;\n    }\n\n    /**\n     * Return the number for this transaction.\n     *\n     * @return the transaction number. Guaranteed to not return null\n     */\n    @NotNull\n    public String getNumber() {\n        String result = EMPTY;\n\n        if (number != null) {\n            result = number;\n        }\n        return result;\n    }\n\n    /**\n     * Calculates the amount of the transaction relative to the supplied account.\n     * <p>\n     * This method is synchronized to protect against concurrency issues\n     *\n     * @param account reference account\n     * @return Amount of this transaction relative to the supplied account\n     */\n    public synchronized BigDecimal getAmount(final Account account) {\n        return transactionEntries.stream().map(transactionEntry\n                -> transactionEntry.getAmount(account)).reduce(BigDecimal.ZERO, BigDecimal::add);\n    }\n\n    /**\n     * Compares two Transactions for ordering. Equality is checked for at the reference level. If a comparison cannot be\n     * determined, the hashCode is used\n     *\n     * @param tran the {@code Transaction} to be compared.\n     * @return the value {@code 0} if the argument Transaction is equal to this Transaction; a value less than\n     * {@code 0} if this Transaction is before the Transaction argument; and a value greater than\n     * {@code 0} if this Transaction is after the Transaction argument.\n     */\n    @Override\n    public int compareTo(final @NotNull Transaction tran) {\n        if (tran == this) {\n            return 0;\n        }\n\n        int result = date.compareTo(tran.date);\n        if (result != 0) {\n            return result;\n        }\n\n        result = getNumber().compareTo(tran.getNumber());\n        if (result != 0) {\n            return result;\n        }\n\n        result = Long.compareUnsigned(timestamp, tran.timestamp);\n        if (result != 0) {\n            return result;\n        }\n\n        result = getAmount(getCommonAccount()).compareTo(tran.getAmount(tran.getCommonAccount()));\n        if (result != 0) {\n            return result;\n        }\n\n        return getUuid().compareTo(tran.getUuid());\n    }\n\n    /**\n     * Returns a sorted defensive copy of the transaction entries.\n     *\n     * @return list of transaction entries\n     */\n    public List<TransactionEntry> getTransactionEntries() {\n\n        // protect against write through by creating a new ArrayList\n        final List<TransactionEntry> list = new ArrayList<>(transactionEntries);\n        Collections.sort(list);\n\n        return list;\n    }\n\n    private List<TransactionEntry> getTransactionEntries(final Account account) {\n        return transactionEntries.stream()\n                .filter(transactionEntry -> transactionEntry.getCreditAccount().equals(account)\n                        || transactionEntry.getDebitAccount().equals(account)).collect(Collectors.toList());\n    }\n\n\n    /**\n     * Return a list of transaction entries with the given tag.\n     *\n     * @param tag TransactionTag to filter for\n     * @return {@code List<TransactionEntry>} of entries with the given tag. An empty list will be\n     * returned if none are found\n     */\n    List<TransactionEntry> getTransactionEntriesByTag(final TransactionTag tag) {\n        return transactionEntries.stream().filter(e -> e.getTransactionTag() == tag).collect(Collectors.toList());\n    }\n\n    /**\n     * Adds a collection of transaction entries.\n     *\n     * @param entries collection of TransactionEntry(s)\n     */\n    public void addTransactionEntries(final Collection<TransactionEntry> entries) {\n        entries.forEach(this::addTransactionEntry);\n    }\n\n    /**\n     * Clears all transaction entries.\n     */\n    public void clearTransactionEntries() {\n        transactionEntries.clear();\n    }\n\n    public LocalDateTime getTimestamp() {\n        if (timeStampDate == null) {\n            timeStampDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());\n        }\n\n        return timeStampDate;\n    }\n\n    @NotNull\n    public TransactionType getTransactionType() {\n        if (size() == 1) {\n            final TransactionEntry entry = transactionEntries.iterator().next();\n\n            if (entry.isSingleEntry()) {\n                return TransactionType.SINGLENTRY;\n            }\n            return TransactionType.DOUBLEENTRY;\n        }\n\n        if (size() > 1) {\n            return TransactionType.SPLITENTRY;\n        }\n\n        return TransactionType.INVALID;\n    }\n\n    /**\n     * Returns the memo for the {@code Transaction}.  If memo was set to be equal to {@value #CONCATENATE}, then a\n     * concatenated version of the {@code TransactionEntry} memos will be returned.  If the {@code Transaction} level\n     * memo is null, that the memo for the first {@code TransactionEntry} is returned\n     *\n     * @return resultant memo\n     */\n    @NotNull\n    public synchronized String getMemo() {\n        if (memo != null) {\n            if (isMemoConcatenated()) {\n                if (concatMemo == null) {\n                    concatMemo = getMemo(getTransactionEntries());\n                }\n                return concatMemo;\n            }\n\n            return memo;\n        }\n        return getTransactionEntries().get(0).getMemo();\n    }\n\n    /**\n     * Returns the {@code Transaction} level memo\n     *\n     * @return the Transaction memo\n     */\n    @Nullable\n    public String getTransactionMemo() {\n        return memo;\n    }\n\n    /**\n     * Returns the concatenated memo given an Account.\n     *\n     * @param account base account to generate a memo for\n     * @return Concatenated string of split entry memos\n     */\n    @NotNull\n    public synchronized String getMemo(@NotNull final Account account) {\n        return getMemo(getTransactionEntries(account));\n    }\n\n    /**\n     * Builds a concatenated memo given a list of TransactionEntries.\n     *\n     * @param transEntries List of {@code TransactionEntry}\n     * @return concatenated memo\n     */\n    public static String getMemo(final List<TransactionEntry> transEntries) {\n        final List<String> memoList = new ArrayList<>();\n\n        // Create an ordered list of unique memos that are not empty\n        transEntries.stream().filter(transactionEntry\n                -> !transactionEntry.getMemo().isEmpty() && !memoList.contains(transactionEntry.getMemo()))\n                .forEachOrdered(transactionEntry -> memoList.add(transactionEntry.getMemo()));\n\n        return String.join(\", \", memoList);\n    }\n\n    public boolean isMemoConcatenated() {\n        return CONCATENATE.equals(memo);\n    }\n\n    /**\n     * Set the memo for the {@code Transaction}.  If set to be equal to {@value #CONCATENATE}, then a concatenated\n     * version will be reported.  If set to null or an empty string, the memo of the first {@code TransactionEntry}\n     * will be reported.  Otherwise, the supplied String will be reported.\n     *\n     * @param memo sets the {@code Transaction} level memo\n     */\n    public synchronized void setMemo(final String memo) {\n\n        // force to null if empty to conserve memory.\n        if (memo != null && memo.isEmpty()) {\n            this.memo = null;\n        } else {\n            this.memo = memo;\n        }\n    }\n\n    /**\n     * Returns a set of all tags associated with the transaction\n     *\n     * @return Set of all Tags\n     */\n    public Set<Tag> getTags() {\n        final Set<Tag> tags = new HashSet<>();\n\n        for (final TransactionEntry entry : transactionEntries) {\n            tags.addAll(entry.getTags());\n        }\n\n        return tags;\n    }\n\n    /**\n     * Returns a set of all tags associated with the specified Account in common.\n     *\n     * @param account common Account\n     * @return Set of all Tags\n     */\n    public Set<Tag> getTags(final Account account) {\n        final Set<Tag> tags = new HashSet<>();\n\n        for (final TransactionEntry entry : getTransactionEntries(account)) {\n            tags.addAll(entry.getTags());\n        }\n\n        return tags;\n    }\n\n    /**\n     * Assigns the specified Tag(s) to all entries\n     *\n     * @param tags Set of tags to assign\n     * @see TransactionEntry#setTags(Collection)\n     */\n    public void setTags(final Collection<Tag> tags) {\n        for (final TransactionEntry entry : transactionEntries) {\n            entry.setTags(tags);\n        }\n    }\n\n    /**\n     * Assigns the specified Tag(s) to all entries of the specified class\n     * @param clazz class to filter by\n     * @param tags Tags to assign\n     * @see TransactionEntry#setTags(Collection)\n     */\n    public void setTags(@NotNull final Class<? extends TransactionEntry> clazz, @NotNull final Collection<Tag> tags) {\n        transactionEntries.stream().filter(clazz::isInstance).forEach(e-> e.setTags(tags));\n    }\n\n    /**\n     * Returns all tags associated with with entries of the specified class\n     * @param clazz class to filter by\n     * @return Set of Tags\n     */\n    public Set<Tag> getTags(@NotNull final Class<? extends TransactionEntry> clazz) {\n        final Set<Tag> tags = new HashSet<>();\n        transactionEntries.stream().filter(clazz::isInstance)\n                          .forEach(transactionEntry -> tags.addAll(transactionEntry.getTags()));\n        return tags;\n    }\n\n    @Nullable\n    public String getFitid() {\n        return fitid;\n    }\n\n    public void setFitid(final String fitid) {\n        this.fitid = fitid;\n    }\n\n    @SuppressWarnings(\"WeakerAccess\")\n    public void setReconciled(@NotNull final Account account, @NotNull final ReconciledState state) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(state);\n\n        for (final TransactionEntry e : transactionEntries) {\n            e.setReconciled(account, state);\n        }\n    }\n\n    public void setReconciled(@NotNull final ReconciledState state) {\n        Objects.requireNonNull(state);\n\n        for (final TransactionEntry e : transactionEntries) {\n            e.setCreditReconciled(state);\n            e.setDebitReconciled(state);\n        }\n    }\n\n    @NotNull\n    public ReconciledState getReconciled(final Account account) {\n        ReconciledState state = ReconciledState.NOT_RECONCILED; // default is not reconciled\n\n        for (final TransactionEntry e : transactionEntries) {\n            if (e.getCreditAccount().equals(account)) {\n                state = e.getCreditReconciled();\n                break;\n            }\n\n            if (e.getDebitAccount().equals(account)) {\n                state = e.getDebitReconciled();\n                break;\n            }\n        }\n\n        return state;\n    }\n\n    /**\n     * Returns an external link to a file.\n     *\n     * @return external path, null if not set\n     */\n    @Nullable\n    public String getAttachment() {\n        return attachment;\n    }\n\n    /**\n     * Sets an external link to a file, the path should be relative to the data file for portability.\n     * May be set to null.\n     *\n     * @param attachment attachment path\n     */\n    public void setAttachment(@Nullable final String attachment) {\n        this.attachment = attachment;\n    }\n\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n\n        final Transaction tran = (Transaction) super.clone();\n\n        tran.concatMemo = null; // force a reset of the concatenated memo, the entries of the clone may change\n\n        tran.timestamp = System.currentTimeMillis();    // force the clone to have a new timestamp\n        tran.timeStampDate = null;                      // clear the cached value\n\n        // deep clone\n        tran.transactionEntries = new HashSet<>(); // deep clone\n\n        for (final TransactionEntry entry : transactionEntries) {\n            tran.addTransactionEntry((TransactionEntry) entry.clone());\n        }\n\n        return tran;\n    }\n\n    @Override\n    public String toString() {\n        final StringBuilder b = new StringBuilder();\n\n        final String lineSep = System.getProperty(\"line.SEPARATOR\");\n\n        b.append(\"Transaction UUID: \").append(getUuid()).append(lineSep);\n        b.append(\"Number:           \").append(getNumber()).append(lineSep);\n        b.append(\"Payee:            \").append(getPayee()).append(lineSep);\n        b.append(\"Memo:             \").append(getMemo()).append(lineSep);\n\n        b.append(lineSep);\n\n        for (final TransactionEntry entry : getTransactionEntries()) {\n            b.append(entry).append(lineSep);\n        }\n\n        return b.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntry.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.JoinTable;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.OrderBy;\nimport javax.persistence.SequenceGenerator;\n\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry\n * <p>\n * Each Transaction entry has an amount for the credit and debit side of the transaction. When the debit and credit\n * account has the same currency, one amount will be the negated value of the other. If the credit and debit accounts do\n * not have the same currency then the amounts will be different to represent the exchanged value at the time of the\n * transaction.\n * <p>\n * If the entry is to be used as an \"single entry / adjustment\" transaction, then the credit and debit account must be\n * set to the same account and the credit and debit amounts must be set to the same value.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class TransactionEntry implements Comparable<TransactionEntry>, Cloneable, Serializable {\n\n    /**\n     * Cache the hash code for the TransactionEntry\n     */\n    private transient int hash = 0;\n\n    @SuppressWarnings(\"unused\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    private long id;\n\n    @Enumerated(EnumType.STRING)\n    private TransactionTag transactionTag = TransactionTag.BANK;\n\n    /**\n     * Account with balance being decreased.\n     */\n    @ManyToOne\n    private Account debitAccount;\n\n    /**\n     * Account with balance being increased.\n     */\n    @ManyToOne\n    private Account creditAccount;\n\n    @Column(precision = 22, scale = 4)\n    private BigDecimal creditAmount = BigDecimal.ZERO;\n\n    @Column(precision = 22, scale = 4)\n    private BigDecimal debitAmount = BigDecimal.ZERO;\n\n    /**\n     * Reconciled state of the transaction.\n     */\n    @Enumerated(EnumType.STRING)\n    private ReconciledState creditReconciled = ReconciledState.NOT_RECONCILED;\n\n    /**\n     * Reconciled state of the debit side of the transaction.\n     */\n    @Enumerated(EnumType.STRING)\n    private ReconciledState debitReconciled = ReconciledState.NOT_RECONCILED;\n\n    /**\n     * Memo for this entry.\n     */\n    @Column(columnDefinition = \"VARCHAR(1024)\")\n    private String memo = \"\";\n\n    @JoinTable\n    @OrderBy(\"name\")\n    @ManyToMany(cascade = {CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)\n    private Set<Tag> tags = new HashSet<>();\n\n    /**\n     * Public constructor.\n     */\n    public TransactionEntry() {\n    }\n\n    /**\n     * Simple constructor for a single entry transaction.\n     *\n     * @param account account for the transaction\n     * @param amount  amount for the transaction\n     */\n    public TransactionEntry(final Account account, final BigDecimal amount) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(amount);\n\n        creditAccount = account;\n        debitAccount = account;\n\n        creditAmount = amount;\n        debitAmount = amount;\n    }\n\n    /**\n     * Simple constructor for a double entry transaction.\n     *\n     * @param creditAccount credit account for the transaction\n     * @param debitAccount  debit account for the transaction\n     * @param amount        amount for the transaction\n     */\n    TransactionEntry(final Account creditAccount, final Account debitAccount, final BigDecimal amount) {\n        Objects.requireNonNull(creditAccount);\n        Objects.requireNonNull(debitAccount);\n        Objects.requireNonNull(amount);\n\n        this.creditAccount = creditAccount;\n        this.debitAccount = debitAccount;\n\n        setAmount(amount.abs());\n    }\n\n    /**\n     * Simple constructor for a double entry transaction with exchange rate.\n     *\n     * @param creditAccount credit account for the transaction\n     * @param debitAccount  debit account for the transaction\n     * @param creditAmount  amount for the transaction\n     * @param debitAmount   amount for the transaction\n     */\n    TransactionEntry(@NotNull final Account creditAccount, @NotNull final Account debitAccount,\n                     @NotNull final BigDecimal creditAmount, @NotNull final BigDecimal debitAmount) {\n        Objects.requireNonNull(creditAccount);\n        Objects.requireNonNull(debitAccount);\n        Objects.requireNonNull(creditAmount);\n        Objects.requireNonNull(debitAmount);\n\n        assert creditAmount.signum() == 1 && debitAmount.signum() == -1;\n\n        this.creditAccount = creditAccount;\n        this.debitAccount = debitAccount;\n\n        this.creditAmount = creditAmount;\n        this.debitAmount = debitAmount;\n    }\n\n    public BigDecimal getCreditAmount() {\n        return creditAmount;\n    }\n\n    public BigDecimal getAmount(@NotNull final Account account) {\n        Objects.requireNonNull(account);\n\n        if (account.equals(creditAccount)) {\n            return creditAmount;\n        } else if (account.equals(debitAccount)) {\n            return debitAmount;\n        } else {\n            return BigDecimal.ZERO;\n        }\n    }\n\n    /**\n     * Shortcut method to set credit and debit amounts.\n     *\n     * @param amount credit amount of the transaction\n     */\n    public final void setAmount(@NotNull final BigDecimal amount) {\n        Objects.requireNonNull(amount);\n\n        if (amount.signum() < 0) {\n            throw new IllegalArgumentException(\"Amount must not be negative\");\n        }\n\n        creditAmount = amount;\n        debitAmount = amount.negate();\n    }\n\n    public Account getCreditAccount() {\n        return creditAccount;\n    }\n\n    ReconciledState getCreditReconciled() {\n        return creditReconciled;\n    }\n\n    public Account getDebitAccount() {\n        return debitAccount;\n    }\n\n    ReconciledState getDebitReconciled() {\n        return debitReconciled;\n    }\n\n    @NotNull\n    public String getMemo() {\n        return memo;\n    }\n\n    public ReconciledState getReconciled(final Account account) {\n        if (account == getDebitAccount()) {\n            return debitReconciled;\n        } else if (account == getCreditAccount()) {\n            return creditReconciled;\n        }\n\n        return ReconciledState.NOT_RECONCILED;\n    }\n\n    public void setCreditAmount(final BigDecimal creditAmount) {\n        Objects.requireNonNull(creditAmount);\n\n        this.creditAmount = creditAmount;\n    }\n\n    public void setCreditAccount(final Account creditAccount) {\n        this.creditAccount = creditAccount;\n    }\n\n    void setCreditReconciled(@NotNull final ReconciledState creditReconciled) {\n        Objects.requireNonNull(creditReconciled);\n\n        this.creditReconciled = creditReconciled;\n    }\n\n    public void setDebitAccount(final Account debitAccount) {\n        this.debitAccount = debitAccount;\n    }\n\n    void setDebitReconciled(@NotNull final ReconciledState debitReconciled) {\n        Objects.requireNonNull(debitReconciled);\n\n        this.debitReconciled = debitReconciled;\n    }\n\n    /**\n     * Sets the memo for the entry.\n     *\n     * @param memo new memo\n     */\n    public void setMemo(final String memo) {\n        if (memo != null) {\n            this.memo = memo;\n        }\n    }\n\n    public void setReconciled(final Account account, final ReconciledState reconciled) {\n        if (account.equals(getCreditAccount())) {\n            setCreditReconciled(reconciled);\n        } else if (account.equals(getDebitAccount())) {\n            setDebitReconciled(reconciled);\n        }\n    }\n\n    public BigDecimal getDebitAmount() {\n        return debitAmount;\n    }\n\n    public void setDebitAmount(@NotNull final BigDecimal debitAmount) {\n        Objects.requireNonNull(debitAmount);\n\n        this.debitAmount = debitAmount;\n    }\n\n    public void setTags(final Collection<Tag> tags) {\n        this.tags.clear();\n        this.tags.addAll(tags);\n    }\n\n    public Set<Tag> getTags() {\n        if (tags != null) {\n            return Collections.unmodifiableSet(tags);\n        }\n        return Collections.emptySet();\n    }\n\n    @Override\n    public int compareTo(@NotNull final TransactionEntry entry) {\n\n        if (this == entry) {\n            return 0;\n        }\n\n        int result = memo.compareTo(entry.getMemo());\n\n        if (result != 0) {\n            return result;\n        }\n\n        result = transactionTag.compareTo(entry.transactionTag);\n\n        if (result != 0) {\n            return result;\n        }\n\n        if (hashCode() > entry.hashCode()) {\n            return 1;\n        }\n        return -1;\n    }\n\n    /**\n     * Check to determine is this is a single entry transaction.\n     *\n     * @return {@code true} if this is a single entry TransactionEntry\n     */\n    boolean isSingleEntry() {\n        return creditAccount.equals(debitAccount) && creditAmount.compareTo(debitAmount) == 0;\n    }\n\n    /**\n     * Returns true if multiple currencies are being used for this entry.\n     * <p>\n     * If the credit and debit accounts have differing currencies, then unless\n     * the currencies are equal in value, the credit and debit amounts should be different.\n     *\n     * @return {@code true} if this is a multi-currency transaction entry\n     */\n    public boolean isMultiCurrency() {\n        return !creditAccount.getCurrencyNode().equals(debitAccount.getCurrencyNode());\n    }\n\n    public void setTransactionTag(final TransactionTag transactionTag) {\n        Objects.requireNonNull(transactionTag);\n\n        this.transactionTag = transactionTag;\n\n    }\n\n    public TransactionTag getTransactionTag() {\n        return transactionTag;\n    }\n\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n        final TransactionEntry e = (TransactionEntry) super.clone();\n\n        // a deep clone for tags is needed for JPA persistence\n        e.tags = new HashSet<>();\n        e.tags.addAll(tags);\n\n        // clones id must be reset\n        e.id = 0;\n\n        return e;\n    }\n\n    @SuppressWarnings(\"BigDecimalEquals\")\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        TransactionEntry that = (TransactionEntry) o;\n        return Objects.equals(transactionTag, that.transactionTag) &&\n                       Objects.equals(debitAccount, that.debitAccount) &&\n                       Objects.equals(creditAccount, that.creditAccount) &&\n                       Objects.equals(creditAmount, that.creditAmount) &&\n                       Objects.equals(debitAmount, that.debitAmount) &&\n                       Objects.equals(creditReconciled, that.creditReconciled) &&\n                       Objects.equals(debitReconciled, that.debitReconciled) &&\n                       Objects.equals(memo, that.memo) &&\n                       Objects.equals(tags, that.tags);\n    }\n\n    @Override\n    public int hashCode() {\n        int h = hash;\n\n        if (h == 0) {\n            h = Objects.hash(transactionTag, debitAccount, creditAccount, creditAmount, debitAmount,\n                    creditReconciled, debitReconciled, memo, tags);\n            hash = h;\n        }\n        return h;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder b = new StringBuilder();\n\n        final String lineSep = System.lineSeparator();\n\n        b.append(\"TransactionEntry hashCode: \").append(hashCode()).append(lineSep);\n        b.append(\"Tag:            \").append(getTransactionTag().name()).append(lineSep);\n        b.append(\"Memo:           \").append(getMemo()).append(lineSep);\n        b.append(\"Debit Account:  \").append(getDebitAccount().getName()).append(lineSep);\n        b.append(\"Credit Account: \").append(getCreditAccount().getName()).append(lineSep);\n        b.append(\"Debit Amount:   \").append(getDebitAmount().toPlainString()).append(lineSep);\n        b.append(\"Credit Amount:  \").append(getCreditAmount().toPlainString()).append(lineSep);\n\n        return b.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryAbstractIncrease.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\n/**\n * Abstract Add transaction.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic abstract class TransactionEntryAbstractIncrease extends AbstractInvestmentTransactionEntry {\n\n    protected TransactionEntryAbstractIncrease() {\n    }\n\n    /**\n     * Returns the number of shares as it would impact\n     * the sum of the investment accounts shares. Useful\n     * for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return getQuantity();\n    }\n\n    @Override\n    public void setCreditAccount(Account creditAccount) {\n        super.setCreditAccount(creditAccount);\n        super.setDebitAccount(creditAccount);\n    }\n\n    @Override\n    public void setDebitAccount(Account debitAccount) {\n        super.setDebitAccount(debitAccount);\n        super.setCreditAccount(debitAccount);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryAddX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Add shares without impacting the cash balance.  This is a single\n * entry transaction\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryAddX extends TransactionEntryAbstractIncrease {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryAddX() {\n    }\n\n    public TransactionEntryAddX(final Account account, final SecurityNode securityNode, final BigDecimal price, final BigDecimal quantity) {\n        setCreditAccount(account);\n        setDebitAccount(account);\n\n        setPrice(price);\n        setQuantity(quantity);\n        setSecurityNode(securityNode);\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.ADDSHARE;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryBuyX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Buy shares and reduce the (cash) balance of an account.\n * <p>\n * The investment account is always assigned to the debit account.\n * <p>\n * If an account other than the investment account is assigned to the credit\n * account, then the balance of the credit account is reduced because the cost\n * of the buy is made against it.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryBuyX extends AbstractInvestmentTransactionEntry {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryBuyX() {\n    }\n\n    /**\n     * Constructor.\n     *\n     * @param account           Credit account\n     * @param investmentAccount Debit / Investment  account\n     * @param securityNode      Security for the transaction\n     * @param price             Price of shares\n     * @param quantity          Number of shares\n     * @param exchangeRate      Exchange rate for the credit account (May be ONE, but may not be null and must be greater than ZERO)\n     */\n    TransactionEntryBuyX(final Account account, final Account investmentAccount, final SecurityNode securityNode, final BigDecimal price, final BigDecimal quantity, final BigDecimal exchangeRate) {\n\n        assert investmentAccount.memberOf(AccountGroup.INVEST);\n        assert exchangeRate != null && exchangeRate.signum() == 1;\n\n        setSecurityNode(securityNode);\n        setPrice(price);\n        setQuantity(quantity);\n\n        setDebitAccount(investmentAccount);\n        setCreditAccount(account);\n\n        if (investmentAccount.equals(account)) { // transaction against the cash balance\n\n            BigDecimal amount = price.multiply(quantity).setScale(investmentAccount.getCurrencyNode().getScale(), MathConstants.roundingMode).negate();\n\n            setCreditAmount(amount);\n            setDebitAmount(amount);\n        } else { // transaction against a different account\n\n            setDebitAmount(BigDecimal.ZERO);\n\n            byte scale = getCreditAccount().getCurrencyNode().getScale();\n\n            if (account.getCurrencyNode().equals(investmentAccount.getCurrencyNode())) {\n                setCreditAmount(price.multiply(quantity.negate()).setScale(scale, MathConstants.roundingMode));\n            } else { // currency exchange\n                setCreditAmount(price.multiply(quantity.negate()).multiply(exchangeRate).setScale(scale, MathConstants.roundingMode));\n            }\n        }\n    }\n\n    /**\n     * Returns the number of shares as it would impact\n     * the sum of the investment accounts shares. Useful\n     * for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return getQuantity();\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.BUYSHARE;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryDividendX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.util.Objects;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Investment dividend.\n * <p>\n * The creditAccount and creditAmount fields are used for the investment account.\n * The debitAccount and debitAmount fields are used for the capital gains (income) account.\n * creditAccount is assumed to be the investment account.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryDividendX extends AbstractInvestmentTransactionEntry {\n\n    /**\n     * No argument constructor for reflection purposes only.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryDividendX() {\n\n    }\n\n    /**\n     * Constructor.\n     *\n     * @param incomeAccount         Debit account\n     * @param investmentAccount     Credit/Investment  account\n     * @param securityNode          Security for the transaction\n     * @param dividend              Dividend received\n     * @param incomeExchangedAmount Exchanged amount for the debit account\n     */\n    TransactionEntryDividendX(final Account incomeAccount, final Account investmentAccount, final SecurityNode securityNode, final BigDecimal dividend, final BigDecimal incomeExchangedAmount) {\n        Objects.requireNonNull(incomeAccount);\n        Objects.requireNonNull(investmentAccount);\n\n        assert investmentAccount.memberOf(AccountGroup.INVEST);\n        assert dividend.signum() >= 0 && incomeExchangedAmount.signum() <= 0;\n\n        setSecurityNode(securityNode);\n\n        /* Dividends do not involve exchange of shares */\n        setPrice(BigDecimal.ZERO);\n        setQuantity(BigDecimal.ZERO);\n\n        setCreditAccount(investmentAccount);\n        setDebitAccount(incomeAccount);\n\n        /* Transaction can be treated as single entry, but double entry is being forced by UI\n         * Single entry support is required for jGnash 1.x in. */\n        if (investmentAccount.equals(incomeAccount)) { // transaction against the cash balance\n            setCreditAmount(dividend); // treat as a single entry transaction\n            setDebitAmount(dividend);\n        } else { // double entry dividend\n            setCreditAmount(dividend); // account balance of investment account not impacted\n            setDebitAmount(incomeExchangedAmount);\n        }\n    }\n\n    @Override\n    public BigDecimal getTotal() {\n        return getCreditAmount();\n    }\n\n    /**\n     * Returns the number of shares as it would impact\n     * the sum of the investment accounts shares. Useful\n     * for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return BigDecimal.ZERO;\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.DIVIDEND;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryMergeX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Remove shares without impacting the cash balance. This is a single entry transaction\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryMergeX extends AbstractInvestmentTransactionEntry {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <p>\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryMergeX() {\n    }\n\n    public TransactionEntryMergeX(final Account investmentAccount, final SecurityNode securityNode, final BigDecimal price, final BigDecimal quantity) {\n\n        if (investmentAccount.getAccountType().getAccountGroup() != AccountGroup.INVEST) {\n            throw new RuntimeException(ResourceUtils.getString(\"Message.Error.InvalidAccountGroup\"));\n        }\n\n        setCreditAccount(investmentAccount);\n        setDebitAccount(investmentAccount);\n\n        setPrice(price);\n        setQuantity(quantity);\n        setSecurityNode(securityNode);\n    }\n\n    /**\n     * Returns the number of shares as it would impact the sum of the investment accounts shares. Useful for summing\n     * share quantities\n     * \n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return getQuantity().negate();\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.MERGESHARE;\n    }\n\n    @Override\n    public void setCreditAccount(final Account creditAccount) {\n        super.setCreditAccount(creditAccount);\n        super.setDebitAccount(creditAccount);\n    }\n\n    @Override\n    public void setDebitAccount(final Account debitAccount) {\n        super.setDebitAccount(debitAccount);\n        super.setCreditAccount(debitAccount);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryReinvestDivX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Reinvest dividend transaction.\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryReinvestDivX extends TransactionEntryAbstractIncrease {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <p>\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryReinvestDivX() {\n    }\n\n    TransactionEntryReinvestDivX(final Account investmentAccount, final SecurityNode securityNode,\n                                 final BigDecimal price, final BigDecimal quantity) {\n\n        if (investmentAccount.getAccountType().getAccountGroup() != AccountGroup.INVEST) {\n            throw new RuntimeException(ResourceUtils.getString(\"Message.Error.InvalidAccountGroup\"));\n        }\n\n        setSecurityNode(securityNode);\n        setPrice(price);\n        setQuantity(quantity);\n        setCreditAccount(investmentAccount);\n        setDebitAmount(BigDecimal.ZERO);\n        setCreditAmount(BigDecimal.ZERO);\n    }\n\n    /**\n     * Returns {@code TransactionType.REINVESTDIV} for this type of transaction\n     * \n     * @see TransactionType#REINVESTDIV\n     * @return returns {@code TransactionType.REINVESTDIV}\n     */\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.REINVESTDIV;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryRemoveX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Remove shares without impacting the cash balance.  This is a single\n * entry transaction\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryRemoveX extends AbstractInvestmentTransactionEntry {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryRemoveX() {\n    }\n\n    public TransactionEntryRemoveX(final Account account, final SecurityNode securityNode, final BigDecimal price, final BigDecimal quantity) {\n        setCreditAccount(account);\n        setDebitAccount(account);\n\n        setPrice(price);\n        setQuantity(quantity);\n        setSecurityNode(securityNode);\n    }\n\n    /**\n     * Returns the number of shares as it would impact\n     * the sum of the investment accounts shares. Useful\n     * for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return getQuantity().negate();\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.REMOVESHARE;\n    }\n\n    @Override\n    public void setCreditAccount(final Account creditAccount) {\n        super.setCreditAccount(creditAccount);\n        super.setDebitAccount(creditAccount);\n    }\n\n    @Override\n    public void setDebitAccount(final Account debitAccount) {\n        super.setDebitAccount(debitAccount);\n        super.setCreditAccount(debitAccount);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntryRocX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.util.Objects;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Return of capital investment transaction.\n * <p>\n * The creditAccount and creditAmount fields are used for the investment account.\n * The debitAccount and debitAmount fields are used for the capital gains (income) account.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntryRocX extends AbstractInvestmentTransactionEntry {\n\n    /**\n     * No argument constructor for reflection purposes only.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntryRocX() {\n    }\n\n    /**\n     * Constructor.\n     *\n     * @param incomeAccount         Debit account\n     * @param investmentAccount     Credit/Investment  account\n     * @param securityNode          Security for the transaction\n     * @param dividend              Dividend received\n     * @param incomeExchangedAmount Exchanged amount for the debit account\n     */\n    TransactionEntryRocX(final Account incomeAccount, final Account investmentAccount, final SecurityNode securityNode, final BigDecimal dividend, final BigDecimal incomeExchangedAmount) {\n        Objects.requireNonNull(incomeAccount);\n        Objects.requireNonNull(investmentAccount);\n\n        assert investmentAccount.memberOf(AccountGroup.INVEST);\n        assert dividend.signum() >= 0 && incomeExchangedAmount.signum() <= 0;\n\n        setSecurityNode(securityNode);\n\n        /* Dividends do not involve exchange of shares */\n        setPrice(BigDecimal.ZERO);\n        setQuantity(BigDecimal.ZERO);\n\n        setCreditAccount(investmentAccount);\n        setDebitAccount(incomeAccount);\n\n        /* Transaction can be treated as single entry, but double entry is being forced by UI\n         * Single entry support is required for jGnash 1.x in. */\n        if (investmentAccount.equals(incomeAccount)) { // transaction against the cash balance\n            setCreditAmount(dividend); // treat as a single entry transaction\n            setDebitAmount(dividend);\n        } else { // double entry dividend\n            setCreditAmount(dividend); // account balance of investment account not impacted\n            setDebitAmount(incomeExchangedAmount);\n        }\n    }\n\n    @Override\n    public BigDecimal getTotal() {\n        return getCreditAmount();\n    }\n\n    /**\n     * Returns the number of shares as it would impact\n     * the sum of the investment accounts shares. Useful\n     * for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return BigDecimal.ZERO;\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.RETURNOFCAPITAL;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntrySellX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\n\n/**\n * Sell shares and increase the (cash) balance of an account.\n * <p>\n * The investment account is always assigned to the credit account.\n * <p>\n * If an account other than the investment account is assigned to the debit\n * account, then the balance of the debit account is increased because the\n * gains of the sell are added to it.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntrySellX extends AbstractInvestmentTransactionEntry {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntrySellX() {\n    }\n\n    /**\n     * Constructor.\n     *\n     * @param account           Debit account\n     * @param investmentAccount Credit / Investment  account\n     * @param securityNode      Security for the transaction\n     * @param price             Price of shares\n     * @param quantity          Number of shares\n     * @param exchangeRate      Exchange rate for the debit account (May be ONE, but may not be null and must be greater than ZERO)\n     */\n    TransactionEntrySellX(final Account account, final Account investmentAccount, final SecurityNode securityNode,\n                          final BigDecimal price, final BigDecimal quantity, final BigDecimal exchangeRate) {\n\n        assert investmentAccount.memberOf(AccountGroup.INVEST);\n        assert exchangeRate != null && exchangeRate.signum() == 1;\n\n        setSecurityNode(securityNode);\n        setPrice(price);\n        setQuantity(quantity);\n\n        setCreditAccount(investmentAccount);\n        setDebitAccount(account);\n\n        if (investmentAccount.equals(account)) { // transaction against the cash balance\n\n            BigDecimal amount = price.multiply(quantity).setScale(investmentAccount.getCurrencyNode().getScale(), MathConstants.roundingMode);\n\n            setCreditAmount(amount);\n            setDebitAmount(amount);\n        } else { // transaction against a different account\n\n            setCreditAmount(BigDecimal.ZERO);\n\n            byte scale = getCreditAccount().getCurrencyNode().getScale();\n\n            if (account.getCurrencyNode().equals(investmentAccount.getCurrencyNode())) {\n                setDebitAmount(price.multiply(quantity).setScale(scale, MathConstants.roundingMode));\n            } else { // currency exchange\n                setDebitAmount(price.multiply(quantity).multiply(exchangeRate).setScale(scale, MathConstants.roundingMode));\n            }\n        }\n    }\n\n    /**\n     * Returns the number of shares as it would impact\n     * the sum of the investment accounts shares. Useful\n     * for summing share quantities\n     *\n     * @return the quantity of securities for this transaction\n     */\n    @Override\n    public BigDecimal getSignedQuantity() {\n        return getQuantity().negate();\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.SELLSHARE;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionEntrySplitX.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\n\nimport javax.persistence.Entity;\n\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Add shares without impacting the cash balance. This is a single entry transaction\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic class TransactionEntrySplitX extends TransactionEntryAbstractIncrease {\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <p>\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    public TransactionEntrySplitX() {\n    }\n\n    public TransactionEntrySplitX(final Account investmentAccount, final SecurityNode securityNode, final BigDecimal price, final BigDecimal quantity) {\n\n        if (investmentAccount.getAccountType().getAccountGroup() != AccountGroup.INVEST) {\n            throw new RuntimeException(ResourceUtils.getString(\"Message.Error.InvalidAccountGroup\"));\n        }\n\n        setCreditAccount(investmentAccount);\n        setDebitAccount(investmentAccount);\n\n        setPrice(price);\n        setQuantity(quantity);\n        setSecurityNode(securityNode);\n    }\n\n    @Override\n    @NotNull\n    public TransactionType getTransactionType() {\n        return TransactionType.SPLITSHARE;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.Collection;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport jgnash.text.NumericFormats;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Transaction Factory.\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionFactory {\n\n    private static final String MESSAGE_ERROR_INVALID_TRANSACTION_TAG = \"Message.Error.InvalidTransactionTag\";\n\n    /**\n     * Create an AddX investment transaction.\n     *\n     * @param investmentAccount Investment account\n     * @param node              Security to add\n     * @param price             Price of each share\n     * @param quantity          Number of shares\n     * @param date              Transaction date\n     * @param memo              Transaction memo\n     * @return new Investment Transaction\n     */\n    public static InvestmentTransaction generateAddXTransaction(final Account investmentAccount,\n                                                                final SecurityNode node, final BigDecimal price,\n                                                                final BigDecimal quantity, final LocalDate date,\n                                                                final String memo) {\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryAddX entry = new TransactionEntryAddX(investmentAccount, node, price, quantity);\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.Add\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    /**\n     * Create a buy security transaction.\n     *\n     * @param account           Account to buy against (can be the same investment account)\n     * @param investmentAccount Investment account\n     * @param node              Security to buy\n     * @param price             Price of each share\n     * @param quantity          Number of shares\n     * @param exchangeRate      Exchange rate (Can be BigDecimal.ONE, cannot be null)\n     * @param date              Transaction date\n     * @param memo              Transaction memo\n     * @param fees              List of transaction fees\n     * @return new Transaction\n     */\n    public static InvestmentTransaction generateBuyXTransaction(final Account account, final Account investmentAccount,\n                                                                final SecurityNode node, final BigDecimal price,\n                                                                final BigDecimal quantity, final BigDecimal exchangeRate,\n                                                                final LocalDate date, final String memo,\n                                                                final Collection<TransactionEntry> fees) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n        Objects.requireNonNull(exchangeRate);\n        Objects.requireNonNull(fees);\n\n        // verify fees are tagged correctly\n        for (TransactionEntry fee : fees) {\n            if (fee.getTransactionTag() != TransactionTag.INVESTMENT_FEE) {\n                throw new EngineException(ResourceUtils.getString(MESSAGE_ERROR_INVALID_TRANSACTION_TAG));\n            }\n        }\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryBuyX entry = new TransactionEntryBuyX(account, investmentAccount, node, price, quantity,\n                exchangeRate);\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.Buy\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        // process transaction fees\n        processFees(transaction, account, investmentAccount, memo, fees, exchangeRate);\n\n        // Logger.getLogger(TransactionFactory.class.getName()).info(transaction.toString());\n\n        return transaction;\n    }\n\n    /**\n     * Create a Dividend transaction.\n     *\n     * @param incomeAccount         Income source account for the cash dividend\n     * @param investmentAccount     Investment account\n     * @param cashAccount           The account receiving the cash dividend. May be the same investment account.\n     * @param node                  Security for dividend\n     * @param dividend              Cash dividend\n     * @param incomeExchangedAmount Income account exchanged amount (Can be the same as dividend, cannot be null)\n     * @param cashExchangedAmount   The exchanged amount for the cash account (Can be the same as dividend, cannot be null)\n     * @param date                  Transaction date\n     * @param memo                  Transaction memo\n     * @return new InvestmentTransaction\n     */\n    public static InvestmentTransaction generateDividendXTransaction(final Account incomeAccount,\n                                                                     final Account investmentAccount,\n                                                                     final Account cashAccount, final SecurityNode node,\n                                                                     final BigDecimal dividend,\n                                                                     final BigDecimal incomeExchangedAmount,\n                                                                     final BigDecimal cashExchangedAmount,\n                                                                     final LocalDate date, final String memo) {\n        Objects.requireNonNull(incomeAccount);\n        Objects.requireNonNull(cashAccount);\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(dividend);\n        Objects.requireNonNull(incomeExchangedAmount);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        if (incomeExchangedAmount.signum() > 0) {\n            throw new EngineException(\"Income exchange amount must be less than or equal to zero\");\n        }\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryDividendX entry = new TransactionEntryDividendX(incomeAccount, investmentAccount, node,\n                dividend, incomeExchangedAmount);\n        entry.setMemo(memo);\n\n        final NumberFormat format = NumericFormats.getFullCommodityFormat(incomeAccount.getCurrencyNode());\n\n        transaction.setPayee(ResourceUtils.getString(\"Word.Dividend\") + \" : \" + node.getSymbol() + \" @ \"\n                + format.format(dividend));\n\n        transaction.addTransactionEntry(entry);\n\n        // Process a cash transfer\n        processCashTransfer(transaction, cashAccount, investmentAccount, memo, dividend, cashExchangedAmount);\n\n        return transaction;\n    }\n\n    /**\n     * Create a Return of Capital transaction.\n     *\n     * @param incomeAccount         Income source account for the cash dividend\n     * @param investmentAccount     Investment account\n     * @param cashAccount           The account receiving the cash dividend. May be the same investment account.\n     * @param node                  Security for dividend\n     * @param dividend              Cash dividend\n     * @param incomeExchangedAmount Income account exchanged amount (Can be the same as dividend, cannot be null)\n     * @param cashExchangedAmount   The exchanged amount for the cash account (Can be the same as dividend, cannot be\n     *                              null)\n     * @param date                  Transaction date\n     * @param memo                  Transaction memo\n     * @return new InvestmentTransaction\n     */\n    public static InvestmentTransaction generateRocXTransaction(final Account incomeAccount,\n                                                                final Account investmentAccount,\n                                                                final Account cashAccount, final SecurityNode node,\n                                                                final BigDecimal dividend,\n                                                                final BigDecimal incomeExchangedAmount,\n                                                                final BigDecimal cashExchangedAmount,\n                                                                final LocalDate date, final String memo) {\n        Objects.requireNonNull(incomeAccount);\n        Objects.requireNonNull(cashAccount);\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(dividend);\n        Objects.requireNonNull(incomeExchangedAmount);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        if (incomeExchangedAmount.signum() > 0) {\n            throw new EngineException(\"Income exchange amount must be less than or equal to zero\");\n        }\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryRocX entry = new TransactionEntryRocX(incomeAccount, investmentAccount, node, dividend,\n                incomeExchangedAmount);\n        entry.setMemo(memo);\n\n        final ResourceBundle rb = ResourceUtils.getBundle();\n\n        final NumberFormat format = NumericFormats.getFullCommodityFormat(incomeAccount.getCurrencyNode());\n\n        transaction.setPayee(rb.getString(\"Word.ReturnOfCapital\") + \" : \" + node.getSymbol() + \" @ \"\n                + format.format(dividend));\n\n        transaction.addTransactionEntry(entry);\n\n        // Process a cash transfer\n        processCashTransfer(transaction, cashAccount, investmentAccount, memo, dividend, cashExchangedAmount);\n\n        return transaction;\n    }\n\n    /**\n     * Generate a double entry transaction with exchange rate.\n     *\n     * @param creditAccount Credit account\n     * @param debitAccount  Debit account\n     * @param creditAmount  Transaction credit amount\n     * @param debitAmount   Transaction credit amount\n     * @param date          Transaction date\n     * @param memo          Transaction memo\n     * @param payee         Transaction payee\n     * @param number        Transaction number\n     * @return new Transaction\n     */\n    public static Transaction generateDoubleEntryTransaction(final Account creditAccount, final Account debitAccount,\n                                                             final BigDecimal creditAmount, final BigDecimal debitAmount,\n                                                             final LocalDate date, final String memo, final String payee,\n                                                             final String number) {\n        Objects.requireNonNull(creditAccount);\n        Objects.requireNonNull(debitAccount);\n\n        if (creditAccount == debitAccount) {\n            throw new EngineException(ResourceUtils.getString(\"Message.Error.CreditDebit.Equal\"));\n        }\n\n        final Transaction transaction = new Transaction();\n\n        transaction.setDate(date);\n        transaction.setNumber(number);\n        transaction.setPayee(payee);\n        transaction.setMemo(memo);\n\n        final TransactionEntry entry = new TransactionEntry(creditAccount, debitAccount, creditAmount, debitAmount);\n        entry.setMemo(memo);\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    /**\n     * Generate a double entry transaction.\n     *\n     * @param creditAccount Credit account\n     * @param debitAccount  Debit account\n     * @param amount        Transaction amount\n     * @param date          Transaction date\n     * @param memo          Transaction memo\n     * @param payee         Transaction payee\n     * @param number        Transaction number\n     * @return new Transaction\n     */\n    public static Transaction generateDoubleEntryTransaction(final Account creditAccount, final Account debitAccount,\n                                                             final BigDecimal amount, final LocalDate date, final String memo,\n                                                             final String payee, final String number) {\n        Objects.requireNonNull(creditAccount);\n        Objects.requireNonNull(debitAccount);\n\n        if (creditAccount == debitAccount) {\n            throw new EngineException(ResourceUtils.getString(\"Message.Error.CreditDebit.Equal\"));\n        }\n\n        final Transaction transaction = new Transaction();\n\n        transaction.setDate(date);\n        transaction.setNumber(number);\n        transaction.setPayee(payee);\n        transaction.setMemo(memo);\n\n        final TransactionEntry entry = new TransactionEntry(creditAccount, debitAccount, amount);\n        entry.setMemo(memo);\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    /**\n     * Create a Split investment transaction.\n     *\n     * @param investmentAccount Investment account\n     * @param node              Security that merged\n     * @param price             Price of each share\n     * @param quantity          Number of shares\n     * @param date              Transaction date\n     * @param memo              Transaction memo\n     * @return new Investment Transaction\n     */\n    public static InvestmentTransaction generateMergeXTransaction(final Account investmentAccount,\n                                                                  final SecurityNode node, final BigDecimal price,\n                                                                  final BigDecimal quantity, final LocalDate date,\n                                                                  final String memo) {\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryMergeX entry = new TransactionEntryMergeX(investmentAccount, node, price, quantity);\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.Merge\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    /**\n     * Create a Reinvested Dividend transaction.\n     *\n     * @param investmentAccount Investment account\n     * @param node              Security for dividend\n     * @param price             Share price\n     * @param quantity          Quantity of shares reinvested\n     * @param date              Date of transaction\n     * @param memo              Transaction memo\n     * @param fees              Fee entry(s)\n     * @param gains             Gain/Loss entry(s)\n     * @return new InvestmentTransaction\n     */\n    public static InvestmentTransaction generateReinvestDividendXTransaction(final Account investmentAccount,\n                                                                             final SecurityNode node,\n                                                                             final BigDecimal price,\n                                                                             final BigDecimal quantity,\n                                                                             final LocalDate date, final String memo,\n                                                                             final Collection<TransactionEntry> fees,\n                                                                             final Collection<TransactionEntry> gains) {\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n        Objects.requireNonNull(fees);\n        Objects.requireNonNull(gains);\n\n        for (final TransactionEntry fee : fees) {\n            if (fee.getTransactionTag() != TransactionTag.INVESTMENT_FEE) {\n                throw new EngineException(ResourceUtils.getString(MESSAGE_ERROR_INVALID_TRANSACTION_TAG));\n            }\n        }\n\n        for (final TransactionEntry gain : gains) {\n            if (gain.getTransactionTag() != TransactionTag.GAIN_LOSS) {\n                throw new EngineException(ResourceUtils.getString(MESSAGE_ERROR_INVALID_TRANSACTION_TAG));\n            }\n        }\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryReinvestDivX entry = new TransactionEntryReinvestDivX(investmentAccount, node, price,\n                quantity);\n\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.ReInvDiv\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        if (!fees.isEmpty()) {\n            BigDecimal totalFees = BigDecimal.ZERO;\n\n            // loop through and add investment fees\n            for (final TransactionEntry fee : fees) {\n                transaction.addTransactionEntry(fee);\n                totalFees = totalFees.add(fee.getAmount(investmentAccount));\n            }\n\n            // create a single entry transaction that offsets any resulting fees\n            final TransactionEntry feesOffsetEntry = new TransactionEntry(investmentAccount, totalFees.negate());\n            feesOffsetEntry.setMemo(memo);\n            feesOffsetEntry.setTransactionTag(TransactionTag.FEES_OFFSET);\n\n            assert feesOffsetEntry.isSingleEntry(); // check\n\n            transaction.addTransactionEntry(feesOffsetEntry);\n        }\n\n        // process gains\n        processGains(transaction, investmentAccount, memo, gains);\n\n        return transaction;\n    }\n\n    /**\n     * Create a RemoveX investment transaction.\n     *\n     * @param investmentAccount Investment account\n     * @param node              Security to remove\n     * @param price             Price of each share\n     * @param quantity          Number of shares\n     * @param date              Transaction date\n     * @param memo              Transaction memo\n     * @return new Investment Transaction\n     */\n    public static InvestmentTransaction generateRemoveXTransaction(final Account investmentAccount,\n                                                                   final SecurityNode node, final BigDecimal price,\n                                                                   final BigDecimal quantity, final LocalDate date,\n                                                                   final String memo) {\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntryRemoveX entry = new TransactionEntryRemoveX(investmentAccount, node, price, quantity);\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.Remove\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    /**\n     * Create a sell security transaction.\n     *\n     * @param account           Account receive sale profits or loss. May be the same as the investment account (Cash balance)\n     * @param investmentAccount Investment account\n     * @param node              Security to sell\n     * @param price             Price of each share\n     * @param quantity          Number of shares\n     * @param exchangeRate      Exchanged amount (cannot be null)\n     * @param date              Transaction date\n     * @param memo              Transaction memo\n     * @param fees              Purchase fee\n     * @param gains             Gains/Loss entries\n     * @return new Transaction\n     */\n    public static InvestmentTransaction generateSellXTransaction(final Account account, final Account investmentAccount,\n                                                                 final SecurityNode node, final BigDecimal price,\n                                                                 final BigDecimal quantity, final BigDecimal exchangeRate,\n                                                                 final LocalDate date, final String memo, final Collection<TransactionEntry> fees,\n                                                                 final Collection<TransactionEntry> gains) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(exchangeRate);\n        Objects.requireNonNull(gains);\n        Objects.requireNonNull(fees);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        // verify fees are tagged correctly\n        for (final TransactionEntry fee : fees) {\n            if (fee.getTransactionTag() != TransactionTag.INVESTMENT_FEE) {\n                throw new EngineException(ResourceUtils.getString(MESSAGE_ERROR_INVALID_TRANSACTION_TAG));\n            }\n        }\n\n        // verify gains are tagged correctly\n        for (final TransactionEntry gain : gains) {\n            if (gain.getTransactionTag() != TransactionTag.GAIN_LOSS) {\n                throw new EngineException(ResourceUtils.getString(MESSAGE_ERROR_INVALID_TRANSACTION_TAG));\n            }\n        }\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntrySellX entry = new TransactionEntrySellX(account, investmentAccount, node, price, quantity,\n                exchangeRate);\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.Sell\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        // process transaction fees\n        processFees(transaction, account, investmentAccount, memo, fees, exchangeRate);\n\n        // process gains\n        processGains(transaction, investmentAccount, memo, gains);\n\n        // Logger.getLogger(TransactionFactory.class.getName()).info(transaction.toString());\n\n        return transaction;\n    }\n\n    /**\n     * Create a single entry transaction.\n     *\n     * @param account Destination account\n     * @param amount  Transaction amount\n     * @param date    Transaction date\n     * @param memo    Transaction memo\n     * @param payee   Transaction payee\n     * @param number  Transaction number\n     * @return new Transaction\n     */\n    public static Transaction generateSingleEntryTransaction(final Account account, final BigDecimal amount,\n                                                             final LocalDate date, final String memo, final String payee,\n                                                             final String number) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(amount);\n        Objects.requireNonNull(date);\n\n        final Transaction transaction = new Transaction();\n\n        transaction.setDate(date);\n        transaction.setNumber(number);\n        transaction.setPayee(payee);\n        transaction.setMemo(memo);\n\n        final TransactionEntry entry = new TransactionEntry(account, amount);\n        entry.setMemo(memo);\n\n        assert entry.isSingleEntry(); // check\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    /**\n     * Create a Split investment transaction.\n     *\n     * @param investmentAccount Investment account\n     * @param node              Security that split\n     * @param price             Price of each share\n     * @param quantity          Number of shares\n     * @param date              Transaction date\n     * @param memo              Transaction memo\n     * @return new Investment Transaction\n     */\n    public static InvestmentTransaction generateSplitXTransaction(final Account investmentAccount,\n                                                                  final SecurityNode node, final BigDecimal price,\n                                                                  final BigDecimal quantity, final LocalDate date,\n                                                                  final String memo) {\n\n        Objects.requireNonNull(investmentAccount);\n        Objects.requireNonNull(node);\n        Objects.requireNonNull(price);\n        Objects.requireNonNull(quantity);\n        Objects.requireNonNull(date);\n        Objects.requireNonNull(memo);\n\n        final InvestmentTransaction transaction = new InvestmentTransaction();\n        transaction.setDate(date);\n        transaction.setMemo(memo);\n\n        final TransactionEntrySplitX entry = new TransactionEntrySplitX(investmentAccount, node, price, quantity);\n        entry.setMemo(memo);\n\n        transaction.setPayee(buildPayee(\"Word.Split\", node, price, quantity));\n\n        transaction.addTransactionEntry(entry);\n\n        return transaction;\n    }\n\n    private static String buildPayee(final String wordProperty, final SecurityNode node, final BigDecimal price,\n                                     final BigDecimal quantity) {\n\n        final NumberFormat format = NumericFormats.getFullCommodityFormat(node);\n\n        return ResourceUtils.getString(wordProperty) + \" : \" + node.getSymbol() + ' ' + quantity.toString() + \" @ \"\n                + format.format(price);\n    }\n\n    private static void processCashTransfer(final Transaction transaction, final Account cashAccount,\n                                            final Account investmentAccount, final String memo,\n                                            final BigDecimal dividend, final BigDecimal cashExchangedAmount) {\n\n        if (!cashAccount.equals(investmentAccount)) {\n            final TransactionEntry tran = new TransactionEntry(cashAccount, investmentAccount, cashExchangedAmount,\n                    dividend.negate());\n\n            tran.setMemo(memo);\n            tran.setTransactionTag(TransactionTag.INVESTMENT_CASH_TRANSFER);\n            transaction.addTransactionEntry(tran);\n        }\n    }\n\n    private static void processFees(final Transaction transaction, final Account account,\n                                    final Account investmentAccount, final String memo,\n                                    final Collection<TransactionEntry> fees, final BigDecimal exchangeRate) {\n        // process transaction fees\n        if (!fees.isEmpty()) {\n\n            // total of the fees charged against the investment account\n            BigDecimal nonCashBalanceFees = BigDecimal.ZERO;\n\n            // loop through and add investment fees to the transaction\n            for (final TransactionEntry fee : fees) {\n                transaction.addTransactionEntry(fee);\n\n                nonCashBalanceFees = nonCashBalanceFees.add(fee.getAmount(investmentAccount).abs());\n            }\n\n            // transfer cash from the account to the cash balance of the investment account to cover fees\n            if (!account.equals(investmentAccount)) {\n\n                byte scale = account.getCurrencyNode().getScale();\n                BigDecimal exchangedAmount = nonCashBalanceFees.abs().multiply(exchangeRate).setScale(scale,\n                        MathConstants.roundingMode);\n\n                final TransactionEntry tran = new TransactionEntry(investmentAccount, account, nonCashBalanceFees,\n                        exchangedAmount.negate());\n                tran.setMemo(memo);\n                tran.setTransactionTag(TransactionTag.INVESTMENT_CASH_TRANSFER);\n                transaction.addTransactionEntry(tran);\n            }\n        }\n    }\n\n    private static void processGains(final Transaction transaction, final Account investmentAccount, final String memo,\n                                     final Collection<TransactionEntry> gains) {\n\n        if (!gains.isEmpty()) { // capital gains entered\n            BigDecimal totalGains = BigDecimal.ZERO;\n\n            // loop through and add gains/loss entries\n            for (final TransactionEntry gain : gains) {\n                transaction.addTransactionEntry(gain);\n                totalGains = totalGains.add(gain.getAmount(investmentAccount));\n            }\n\n            // create a single entry transaction that offsets investment gains or loss\n            final TransactionEntry gainsOffsetEntry = new TransactionEntry(investmentAccount, totalGains.negate());\n            gainsOffsetEntry.setMemo(memo);\n            gainsOffsetEntry.setTransactionTag(TransactionTag.GAINS_OFFSET);\n\n            assert gainsOffsetEntry.isSingleEntry(); // check\n\n            transaction.addTransactionEntry(gainsOffsetEntry);\n        }\n\n    }\n\n    public static TransactionEntry createTransactionEntry(final Account debitAccount, final Account creditAccount,\n                                                           final BigDecimal amount, final String memo,\n                                                           final TransactionTag transactionTag) {\n        final TransactionEntry entry = new TransactionEntry();\n\n        entry.setMemo(memo);\n\n        entry.setDebitAccount(debitAccount);\n        entry.setCreditAccount(creditAccount);\n\n        entry.setDebitAmount(amount.abs().negate());\n        entry.setCreditAmount(amount.abs());\n\n        entry.setTransactionTag(transactionTag);\n        return entry;\n    }\n\n    private TransactionFactory() {\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionTag.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Tagging enumeration for special transaction types.\n * \n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"UnusedDeclaration\")\npublic enum TransactionTag {\n\n    BANK(ResourceUtils.getString(\"Tag.Bank\")),\n    DIVIDEND(ResourceUtils.getString(\"Tag.Dividend\")),\n    FEES_OFFSET(ResourceUtils.getString(\"Tag.FeesOffset\")),\n    GAIN_LOSS(ResourceUtils.getString(\"Tag.GainLoss\")),\n    GAINS_OFFSET(ResourceUtils.getString(\"Tag.GainsOffset\")),\n    INVESTMENT(ResourceUtils.getString(\"Tag.Investment\")),\n    INVESTMENT_FEE(ResourceUtils.getString(\"Tag.InvestmentFee\")),\n    INVESTMENT_CASH_TRANSFER(ResourceUtils.getString(\"Tag.InvestmentCashTransfer\")),\n    VAT(ResourceUtils.getString(\"Tag.Vat\"));\n\n    private final transient String description;\n\n    TransactionTag(String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TransactionType.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Transaction type class.\n *\n * @author Craig Cavanaugh\n *\n */\npublic enum TransactionType {\n\n    // bank transactions\n    DOUBLEENTRY(ResourceUtils.getString(\"Transaction.DoubleEntry\")),\n    SPLITENTRY(ResourceUtils.getString(\"Transaction.SplitEntry\")),\n    SINGLENTRY(ResourceUtils.getString(\"Transaction.SingleEntry\")),\n\n    // investment transactions\n    ADDSHARE(ResourceUtils.getString(\"Transaction.AddShare\")),\n    BUYSHARE(ResourceUtils.getString(\"Transaction.BuyShare\")),\n    DIVIDEND(ResourceUtils.getString(\"Transaction.Dividend\")),\n    REINVESTDIV(ResourceUtils.getString(\"Transaction.ReinvestDiv\")),\n    REMOVESHARE(ResourceUtils.getString(\"Transaction.RemoveShare\")),\n    RETURNOFCAPITAL(ResourceUtils.getString(\"Transaction.ReturnOfCapital\")),\n    SELLSHARE(ResourceUtils.getString(\"Transaction.SellShare\")),\n    SPLITSHARE(ResourceUtils.getString(\"Transaction.SplitShare\")),\n    MERGESHARE(ResourceUtils.getString(\"Transaction.MergeShare\")),\n\n    INVALID(ResourceUtils.getString(\"Word.Invalid\"));\n\n    private final transient String transactionTypeName;\n\n    TransactionType(String name) {\n        transactionTypeName = name;\n    }\n\n    @Override\n    public String toString() {\n        return transactionTypeName;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/TrashObject.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.time.LocalDateTime;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Entity;\nimport javax.persistence.OneToOne;\n\nimport jgnash.util.NotNull;\n\n/**\n * Wraps objects that have been removed from active use in the engine.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class TrashObject extends StoredObject implements Comparable<TrashObject> {\n\n    /**\n     * Date object was added.\n     */\n    private final LocalDateTime date = LocalDateTime.now();\n\n    /**\n     * The stored object.\n     */\n    @OneToOne(orphanRemoval = true, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})\n    private StoredObject object;\n\n    /**\n     * Public no-argument constructor for reflection purposes.\n     */\n    @SuppressWarnings(\"unused\")\n    public TrashObject() {\n    }\n\n    TrashObject(final StoredObject object) {\n        this.object = object;\n        object.setMarkedForRemoval();\n        setMarkedForRemoval();\n    }\n\n    public StoredObject getObject() {\n        return object;\n    }\n\n    public LocalDateTime getDate() {\n        return date;\n    }\n\n    @Override\n    public int compareTo(@NotNull final TrashObject o) {\n        return date.compareTo(o.date);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/attachment/AttachmentManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.attachment;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.concurrent.Future;\n\n/**\n * Interface for handling attachments.\n *\n * @author Craig Cavanaugh\n */\npublic interface AttachmentManager {\n    boolean addAttachment(Path path, boolean copy) throws IOException;\n\n    boolean removeAttachment(String attachment);\n\n    Future<Path> getAttachment(String attachment);\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/attachment/AttachmentTransferClient.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.attachment;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.base64.Base64Decoder;\nimport io.netty.handler.codec.base64.Base64Encoder;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.util.CharsetUtil;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.net.ConnectionFactory;\nimport jgnash.util.EncryptionManager;\n\nimport static jgnash.engine.attachment.NettyTransferHandler.*;\n\n/**\n * Client for sending and receiving files.\n *\n * @author Craig Cavanaugh\n */\nclass AttachmentTransferClient {\n    private static final Logger logger = Logger.getLogger(AttachmentTransferClient.class.getName());\n\n    private final Path tempDirectory;\n\n    private NioEventLoopGroup eventLoopGroup;\n\n    private Channel channel;\n\n    private NettyTransferHandler transferHandler;\n\n    private EncryptionManager encryptionManager = null;\n\n    AttachmentTransferClient(final Path tempPath) {\n        tempDirectory = tempPath;\n    }\n\n    /**\n     * Starts the connection with the lock server.\n     *\n     * @param host remote host\n     * @param port connection port\n     * @param password connection password\n     * @return {@code true} if successful\n     */\n    boolean connectToServer(final String host, final int port, final char[] password) {\n        boolean result = false;\n\n        // If a password has been specified, create an EncryptionManager\n        if (password != null && password.length > 0) {\n            encryptionManager = new EncryptionManager(password);\n        }\n\n        final Bootstrap bootstrap = new Bootstrap();\n\n        eventLoopGroup = new NioEventLoopGroup();\n\n        transferHandler = new NettyTransferHandler(tempDirectory, encryptionManager);\n\n        bootstrap.group(eventLoopGroup)\n                .channel(NioSocketChannel.class)\n                .handler(new Initializer())\n                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ConnectionFactory.getConnectionTimeout() * 1000)\n                .option(ChannelOption.SO_KEEPALIVE, true);\n\n        try {\n            // Start the connection attempt.\n            channel = bootstrap.connect(host, port).sync().channel();\n\n            result = true;\n            logger.info(\"Connection made with File Transfer Server\");\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, \"Failed to connect to the File Transfer Server\", e);\n            disconnectFromServer();\n            Thread.currentThread().interrupt();\n        }\n\n        return result;\n    }\n\n    private String encrypt(final String message) {\n        if (encryptionManager != null) {\n            return encryptionManager.encrypt(message);\n        }\n        return message;\n    }\n\n    void requestFile(final Path file) {\n        try {\n            channel.writeAndFlush(encrypt(FILE_REQUEST + file) + EOL_DELIMITER).sync();\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            Thread.currentThread().interrupt();\n        }\n    }\n\n    void deleteFile(final String attachment) {\n        try {\n            channel.writeAndFlush(encrypt(DELETE + Paths.get(attachment).getFileName()) + EOL_DELIMITER).sync();\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            Thread.currentThread().interrupt();\n        }\n    }\n\n    Future<Void> sendFile(final Path file) {\n        if (transferHandler != null) {\n            return transferHandler.sendFile(channel, file.toString());\n        }\n\n        return null;\n    }\n\n    /**\n     * Disconnects from the lock server.\n     */\n    void disconnectFromServer() {\n\n        try {\n            channel.close().sync();\n        } catch (InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            Thread.currentThread().interrupt();\n        }\n\n        eventLoopGroup.shutdownGracefully();\n\n        eventLoopGroup = null;\n        channel = null;\n\n        logger.info(\"Disconnected from the File Transfer Server\");\n    }\n\n    private class Initializer extends ChannelInitializer<SocketChannel> {\n\n        @Override\n        public void initChannel(final SocketChannel ch) {\n\n            ch.pipeline().addLast(\n                    new DelimiterBasedFrameDecoder(((TRANSFER_BUFFER_SIZE + 2) / 3) * 4 + PATH_MAX,\n                            true, Delimiters.lineDelimiter()),\n\n                    new StringEncoder(CharsetUtil.UTF_8),\n                    new StringDecoder(CharsetUtil.UTF_8),\n\n                    new Base64Encoder(),\n                    new Base64Decoder(),\n\n                    transferHandler);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/attachment/AttachmentTransferServer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.attachment;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.group.ChannelGroup;\nimport io.netty.channel.group.DefaultChannelGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.base64.Base64Decoder;\nimport io.netty.handler.codec.base64.Base64Encoder;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.util.CharsetUtil;\nimport io.netty.util.concurrent.GlobalEventExecutor;\n\nimport java.nio.file.Path;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.util.EncryptionManager;\n\nimport static jgnash.engine.attachment.NettyTransferHandler.PATH_MAX;\nimport static jgnash.engine.attachment.NettyTransferHandler.TRANSFER_BUFFER_SIZE;\n\n/**\n * File server for attachments.\n *\n * @author Craig Cavanaugh\n */\npublic class AttachmentTransferServer {\n\n    private static final Logger logger = Logger.getLogger(AttachmentTransferServer.class.getName());\n\n    private final int port;\n\n    private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();\n\n    private final ChannelGroup channelGroup = new DefaultChannelGroup(\"file-server\", GlobalEventExecutor.INSTANCE);\n\n    private final Path attachmentPath;\n\n    private EncryptionManager encryptionManager = null;\n\n    public AttachmentTransferServer(final int port, final Path attachmentPath) {\n        this.port = port;\n        this.attachmentPath = attachmentPath;\n    }\n\n    public boolean startServer(final char[] password) {\n        boolean result = false;\n\n        // If a password has been specified, create an EncryptionManager\n        if (password != null && password.length > 0) {\n            encryptionManager = new EncryptionManager(password);\n        }\n\n        try {\n            ServerBootstrap b = new ServerBootstrap();\n            b.group(eventLoopGroup)\n                    .channel(NioServerSocketChannel.class)\n                    .option(ChannelOption.SO_KEEPALIVE, true)\n                    //.handler(new LoggingHandler(LogLevel.INFO))   // for debugging purposes\n                    .childHandler(new ChannelInitializer<SocketChannel>() {\n\n                        @Override\n                        public void initChannel(final SocketChannel ch) {\n\n                            ch.pipeline().addLast(\n                                    new DelimiterBasedFrameDecoder(((TRANSFER_BUFFER_SIZE + 2) / 3) * 4 + PATH_MAX,\n                                            true, Delimiters.lineDelimiter()),\n\n                                    new StringEncoder(CharsetUtil.UTF_8),\n                                    new StringDecoder(CharsetUtil.UTF_8),\n\n                                    new Base64Encoder(),\n                                    new Base64Decoder(),\n\n                                    new ServerTransferHandler());\n                        }\n                    });\n\n            // Start the server.\n            final ChannelFuture future = b.bind(port).sync();\n\n            if (future.isDone() && future.isSuccess()) {\n                result = true;\n                logger.info(\"File Transfer Server started successfully\");\n            } else {\n                logger.info(\"Failed to start the File Transfer Server\");\n            }\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            stopServer();\n        }\n\n        return result;\n    }\n\n    public void stopServer() {\n        try {\n            channelGroup.close().sync();\n\n            eventLoopGroup.shutdownGracefully();\n\n            logger.info(\"File Transfer Server stopped\");\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private final class ServerTransferHandler extends NettyTransferHandler {\n\n        ServerTransferHandler() {\n            super(attachmentPath, encryptionManager);\n        }\n\n        @Override\n        public void channelActive(final ChannelHandlerContext ctx) {\n            channelGroup.add(ctx.channel()); // maintain channels\n\n            logger.log(Level.INFO, \"Remote connection from: {0}\", ctx.channel().remoteAddress());\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/attachment/DistributedAttachmentManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.attachment;\n\nimport java.io.IOException;\nimport java.nio.file.DirectoryStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardCopyOption;\nimport java.nio.file.attribute.FileAttribute;\nimport java.nio.file.attribute.PosixFilePermission;\nimport java.nio.file.attribute.PosixFilePermissions;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.EnumSet;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.util.FileUtils;\nimport jgnash.resource.util.OS;\n\n/**\n * Attachment handler for a remote database.\n *\n * @author Craig Cavanaugh\n */\npublic class DistributedAttachmentManager implements AttachmentManager {\n\n    private static final String TEMP_ATTACHMENT_PATH = \"jGnashTemp-\";\n\n    private static final int TRANSFER_TIMEOUT = 5000;\n\n    private final ExecutorService executorService = Executors.newCachedThreadPool();\n\n    private final String host;\n\n    private final int port;\n\n    /**\n     * Path to temporary attachment cache location.\n     */\n    private Path tempAttachmentPath;\n\n    private AttachmentTransferClient fileClient;\n\n    public DistributedAttachmentManager(final String host, final int port) {\n        this.host = host;\n        this.port = port;\n\n        try {\n            if (!OS.isSystemWindows()) {\n                final EnumSet<PosixFilePermission> permissions = EnumSet.of(PosixFilePermission.OWNER_READ,\n                        PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE,\n                        PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE);\n\n                final FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(permissions);\n\n                tempAttachmentPath = Files.createTempDirectory(TEMP_ATTACHMENT_PATH, attr);\n            } else {    // windows cannot handle posix permissions\n                tempAttachmentPath = Files.createTempDirectory(TEMP_ATTACHMENT_PATH);\n            }\n\n            fileClient = new AttachmentTransferClient(tempAttachmentPath);\n        } catch (final IOException e) {\n            Logger.getLogger(DistributedAttachmentManager.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    /**\n     * Add a file attachment.\n     * When moving a file, it must be copied and then deleted.  Moves can not be done atomically across file systems\n     * which is a high probability.\n     *\n     * @param path Path to the attachment to add\n     * @param copy true if only copying the file\n     * @return true if successful\n     * @throws IOException thrown if a network error occurs\n     */\n    @Override\n    public boolean addAttachment(final Path path, final boolean copy) throws IOException {\n\n        boolean result = false;\n\n        // Transfer the file to the remote location\n        final Future<Void> future = fileClient.sendFile(path);\n\n        // Determine the cache location the file needs to go to so it does not have to be requested\n        final Path newPath = Paths.get(tempAttachmentPath + FileUtils.SEPARATOR + path.getFileName());\n\n        if (future != null) {   // if null, path was not valid\n            try {\n                future.get();  // wait for the transfer to complete\n            } catch (final InterruptedException | ExecutionException e) {\n                Logger.getLogger(DistributedAttachmentManager.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n\n            // Copy or move the file\n            if (copy) {\n                Files.copy(path, newPath, StandardCopyOption.REPLACE_EXISTING);\n            } else {\n                Files.copy(path, newPath, StandardCopyOption.REPLACE_EXISTING);\n                Files.delete(path);\n            }\n\n            result = true;\n        }\n\n        return result;\n    }\n\n    @Override\n    public boolean removeAttachment(final String attachment) {\n        fileClient.deleteFile(attachment);\n        return true;\n    }\n\n    @Override\n    public Future<Path> getAttachment(final String attachment) {\n\n        return executorService.submit(() -> {\n            Path path = Paths.get(tempAttachmentPath + FileUtils.SEPARATOR + Paths.get(attachment).getFileName());\n\n            if (Files.notExists(path)) {\n                fileClient.requestFile(Paths.get(attachment));  // Request the file and place in a a temp location\n\n                final LocalDateTime start = LocalDateTime.now();\n\n                while (Duration.between(start, LocalDateTime.now()).toMillis() < TRANSFER_TIMEOUT) {\n                    if (Files.exists(path)) {\n                        break;\n                    }\n                }\n            }\n\n            if (Files.notExists(path)) {\n                path = null;\n            }\n\n            return path;\n        });\n    }\n\n    public boolean connectToServer(final char[] password) {\n        return fileClient.connectToServer(host, port, password);\n    }\n\n    public void disconnectFromServer() {\n        fileClient.disconnectFromServer();\n\n        // Cleanup before exit\n        try (final DirectoryStream<Path> ds = Files.newDirectoryStream(tempAttachmentPath)) {\n            for (final Path p : ds) {\n                Files.delete(p);\n            }\n\n            Files.delete(tempAttachmentPath);\n        } catch (final IOException e) {\n            Logger.getLogger(DistributedAttachmentManager.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/attachment/LocalAttachmentManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.attachment;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport jgnash.engine.AttachmentUtils;\nimport jgnash.engine.EngineFactory;\nimport jgnash.util.FileUtils;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Attachment handler for a local database.\n *\n * @author Craig Cavanaugh\n */\npublic class LocalAttachmentManager implements AttachmentManager {\n\n    private final ExecutorService executorService = Executors.newCachedThreadPool();\n\n    /**\n     * Add a file attachment.\n     * When moving a file, it must be copied and then deleted.  Moves can not be done atomically across file systems\n     * which is a high probability.\n     *\n     * @param path Path to the attachment to add\n     * @param copy true if only copying the file\n     * @return true if successful\n     * @throws IOException thrown if a filesystem error occurs\n     */\n    @Override\n    public boolean addAttachment(final Path path, final boolean copy) throws IOException {\n\n        boolean result = false;\n\n        Path baseFile = Paths.get(EngineFactory.getActiveDatabase());\n\n        if (AttachmentUtils.createAttachmentDirectory(baseFile)) {  // create if needed\n\n            Path newPath = Paths.get(AttachmentUtils.getAttachmentPath() + FileUtils.SEPARATOR + path.getFileName());\n\n            try {\n                if (copy) {\n                    Files.copy(path, newPath);\n                } else {\n                    Files.copy(path, newPath);\n                    Files.delete(path);\n                }\n                result = true;\n            } catch (final IOException e) {\n                logSevere(LocalAttachmentManager.class, e);\n                throw new IOException(e);\n            }\n        }\n\n        return result;\n    }\n\n    @Override\n    public boolean removeAttachment(final String attachment) {\n        boolean result = false;\n\n        Path path = Paths.get(AttachmentUtils.getAttachmentPath() + FileUtils.SEPARATOR + attachment);\n\n        try {\n            Files.delete(path);\n            result = true;\n        } catch (final IOException e) {\n            logSevere(LocalAttachmentManager.class, e);\n        }\n\n        return result;\n    }\n\n    @Override\n    public Future<Path> getAttachment(final String attachment) {\n        return executorService.submit(() ->\n                Paths.get(AttachmentUtils.getAttachmentPath() + FileUtils.SEPARATOR + attachment));\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/attachment/NettyTransferHandler.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.attachment;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.AttachmentUtils;\nimport jgnash.util.EncryptionManager;\nimport jgnash.util.FileUtils;\nimport jgnash.util.Nullable;\n\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.SimpleChannelInboundHandler;\n\n/**\n * Handles the details of bi-directional transfer of files between a client and server.\n *\n * @author Craig Cavanaugh\n */\n@ChannelHandler.Sharable\nclass NettyTransferHandler extends SimpleChannelInboundHandler<String> {\n\n    static final String FILE_REQUEST = \"<FILE_REQUEST>\";\n\n    static final String DELETE = \"<DELETE>\";\n\n    static final String EOL_DELIMITER = \"\\r\\n\";\n\n    private static final String FILE_STARTS = \"<FILE_STARTS>\";\n\n    private static final String FILE_ENDS = \"<FILE_ENDS>\";\n\n    private static final String FILE_CHUNK = \"<FILE_CHUNK>\";\n\n    private static final String ERROR = \"<ERROR>\";\n\n    private static final Logger logger = Logger.getLogger(NettyTransferHandler.class.getName());\n\n    static final int TRANSFER_BUFFER_SIZE = 1024; // too large can break netty... bug?\n\n    static final int PATH_MAX = 4096;\n\n    private final Map<String, Attachment> fileMap = new ConcurrentHashMap<>();\n\n    private final Path attachmentPath;\n\n    private final EncryptionManager encryptionManager;\n\n    /**\n     * Netty Handler.  The specified path may be a temporary location for clients or a persistent location for servers.\n     *\n     * @param attachmentPath Path for attachments.\n     * @param encryptionManager encryption manager instance\n     */\n    NettyTransferHandler(final Path attachmentPath, @Nullable final EncryptionManager encryptionManager) {\n        Objects.requireNonNull(attachmentPath);\n\n        this.attachmentPath = attachmentPath;\n        this.encryptionManager = encryptionManager;\n    }\n\n    @Override\n    public void channelRead0(final ChannelHandlerContext ctx, final String msg) {\n\n        final String plainMessage;\n\n        if (encryptionManager != null) {\n            plainMessage = encryptionManager.decrypt(msg);\n        } else {\n            plainMessage = msg;\n        }\n\n        if (plainMessage.startsWith(FILE_REQUEST)) {\n            sendFile(ctx, attachmentPath + FileUtils.SEPARATOR + plainMessage.substring(FILE_REQUEST.length()));\n        } else if (plainMessage.startsWith(FILE_STARTS)) {\n            openOutputStream(plainMessage.substring(FILE_STARTS.length()));\n        } else if (plainMessage.startsWith(FILE_CHUNK)) {\n            writeOutputStream(plainMessage.substring(FILE_CHUNK.length()));\n        } else if (plainMessage.startsWith(FILE_ENDS)) {\n            closeOutputStream(plainMessage.substring(FILE_ENDS.length()));\n        } else if (plainMessage.startsWith(DELETE)) {\n            deleteFile(plainMessage.substring(FILE_ENDS.length()));\n        }\n    }\n\n    private void deleteFile(final String fileName) {\n        Path path = Paths.get(attachmentPath + FileUtils.SEPARATOR + fileName);\n\n        if (Files.exists(path)) {\n            try {\n                Files.delete(path);\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }\n    }\n\n    @Override\n    public void channelInactive(final ChannelHandlerContext ctx) {\n        for (Attachment object : fileMap.values()) {\n            try {\n                object.outputStream.close();\n            } catch (IOException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }\n\n        ctx.fireChannelInactive();    // forward to the next handler in the pipeline\n    }\n\n    @Override\n    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {\n        super.exceptionCaught(ctx, cause);\n        logger.log(Level.WARNING, \"Unexpected exception from downstream.\", cause);\n        ctx.close();\n    }\n\n    private String encrypt(final String message) {\n        if (encryptionManager != null) {\n            return encryptionManager.encrypt(message);\n        }\n        return message;\n    }\n\n    /**\n     * Sends a file across the channel. The Fu\n     * @param channel  Channel to send file through\n     * @param fileName the file name\n     * @return the future of the potentially asynchronous send is returned. A null value is returned if fileName is a path.\n     */\n    Future<Void> sendFile(final Channel channel, final String fileName) {\n        Future<Void> future = null;\n\n        Path path = Paths.get(fileName);\n\n        if (Files.exists(path)) {\n\n            if (Files.isDirectory(path)) {\n                channel.writeAndFlush(encrypt(ERROR + \"Not a file: \" + path) + EOL_DELIMITER);\n                return null;\n            }\n\n            try (final InputStream inputStream = Files.newInputStream(path)) {\n                channel.writeAndFlush(encrypt(FILE_STARTS + path.getFileName() + \":\" + Files.size(path)) + EOL_DELIMITER);\n\n                byte[] bytes = new byte[TRANSFER_BUFFER_SIZE];  // leave room for base 64 expansion\n\n                int bytesRead;\n\n                while ((bytesRead = inputStream.read(bytes)) != -1) {\n                    if (bytesRead > 0) {\n                        channel.write(encrypt(FILE_CHUNK + path.getFileName() + ':' +\n                                Base64.getEncoder().encodeToString(Arrays.copyOfRange(bytes, 0, bytesRead)))\n                                + EOL_DELIMITER);\n                    }\n                }\n                future = channel.writeAndFlush(encrypt(FILE_ENDS + path.getFileName()) + EOL_DELIMITER).sync();\n\n            } catch (IOException | InterruptedException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                Thread.currentThread().interrupt();\n            }\n        } else {\n            try {\n                future = channel.writeAndFlush(encrypt(ERROR + \"File not found: \" + path) + EOL_DELIMITER).sync();\n            } catch (final InterruptedException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                Thread.currentThread().interrupt();\n            }\n            logger.log(Level.WARNING, \"File not found: {0}\", path);\n        }\n\n        return future;\n    }\n\n    private void sendFile(final ChannelHandlerContext ctx, final String msg) {\n        sendFile(ctx.channel(), msg);\n    }\n\n    private void closeOutputStream(final String msg) {\n        Attachment attachment = fileMap.get(msg);\n\n        try {\n            attachment.outputStream.close();\n            fileMap.remove(msg);\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        if (attachment.path.toFile().length() != attachment.fileSize) {\n            logger.severe(\"Invalid file length\");\n        }\n    }\n\n    private void writeOutputStream(final String msg) {\n        String[] msgParts = msg.split(\":\");\n\n        Attachment attachment = fileMap.get(msgParts[0]);\n\n        if (attachment != null) {\n            try {\n                attachment.outputStream.write(Base64.getDecoder().decode(msgParts[1]));\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }\n    }\n\n    private void openOutputStream(final String msg) {\n        String[] msgParts = msg.split(\":\");\n\n        final String fileName = msgParts[0];\n        final long fileLength = Long.parseLong(msgParts[1]);\n\n        final Path filePath = Paths.get(attachmentPath + FileUtils.SEPARATOR + fileName);\n\n        // Lazy creation of the attachment path if needed\n        if (!AttachmentUtils.createAttachmentDirectory(attachmentPath)) {\n            logger.severe(\"Unable to find or create the attachment directory\");\n            return;\n        }\n\n        try {\n            fileMap.put(fileName, new Attachment(filePath, fileLength));\n        } catch (IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private static class Attachment {\n        final Path path;\n\n        final OutputStream outputStream;\n\n        final long fileSize;\n\n        private Attachment(final Path path, long fileSize) throws IOException {\n            this.path = path;\n            outputStream = Files.newOutputStream(path);\n\n            this.fileSize = fileSize;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/Budget.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport java.math.RoundingMode;\nimport java.time.Month;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.FetchType;\nimport javax.persistence.JoinTable;\nimport javax.persistence.OneToMany;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.StoredObject;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.Period;\nimport jgnash.util.NotNull;\n\n/**\n * Budget Object.\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"JpaDataSourceORMInspection\")\n@Entity\npublic class Budget extends StoredObject implements Comparable<Budget>, Cloneable {\n\n    /**\n     * Budget name.\n     */\n    private String name = \"Default\";\n\n    /**\n     * Budget description.\n     */\n    private String description = \"\";\n\n    /**\n     * Period to report the budget in.\n     */\n    @Enumerated(EnumType.STRING)\n    @Column(name = \"BUDGETPERIOD\")\n    private Period budgetPeriod = Period.MONTHLY;\n\n    /**\n     * Rounding mode for display of budget values.\n     * @since 3.1\n     */\n    @Enumerated(EnumType.STRING)\n    @Column(name = \"ROUNDINGMODE\", length = 12, nullable = false, columnDefinition = \"varchar(12) default 'FLOOR'\")\n    private RoundingMode roundingMode = RoundingMode.FLOOR;\n\n    /**\n     * Controls the number of decimals displayed for budgeting\n     * @since 3.1\n     */\n    @Column(name = \"ROUNDINGSCALE\", nullable = false, columnDefinition = \"tinyint default 2\")\n    private byte roundingScale = 2;\n\n    @Enumerated(EnumType.STRING)\n    @Column(name = \"STARTMONTH\", length = 10, nullable = false, columnDefinition = \"varchar(10) default 'JANUARY'\")\n    private Month startMonth = Month.JANUARY;\n\n    /**\n     * Account goals are stored internally by the account UUID.\n     */\n    @JoinTable\n    @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)\n    private Map<String, BudgetGoal> accountGoals = new HashMap<>();\n\n    private boolean assetAccountsIncluded = false;\n\n    private boolean incomeAccountsIncluded = true;\n\n    private boolean expenseAccountsIncluded = true;\n\n    private boolean liabilityAccountsIncluded = false;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(final String name) {\n        Objects.requireNonNull(name);\n\n        if (name.isEmpty()) {\n            throw new IllegalArgumentException(\"name may not be zero length\");\n        }\n\n        this.name = name;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(final String description) {\n        this.description = Objects.requireNonNull(description);\n    }\n\n    /**\n     * Sets the goals for an {@code Account}.\n     *\n     * @param account    Account\n     * @param budgetGoal budget goals\n     */\n    public void setBudgetGoal(final Account account, final BudgetGoal budgetGoal) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(budgetGoal);\n\n        accountGoals.put(account.getUuid().toString(), budgetGoal);\n    }\n\n    public void removeBudgetGoal(final Account account) {\n        Objects.requireNonNull(account);\n\n        accountGoals.remove(account.getUuid().toString());\n    }\n\n    /**\n     * Returns an accounts goals. If goals have not yet been specified, then an empty set is automatically assigned and\n     * returned\n     *\n     * @param account {@code Account} to retrieve the goals for\n     * @return the goals\n     */\n    public BudgetGoal getBudgetGoal(final Account account) {\n        Objects.requireNonNull(account);\n\n        BudgetGoal goal = accountGoals.get(account.getUuid().toString());\n\n        if (goal == null) {\n            goal = new BudgetGoal();\n            goal.setBudgetPeriod(getBudgetPeriod());\n\n            accountGoals.put(account.getUuid().toString(), goal);\n        }\n\n        return goal;\n    }\n\n    @Override\n    public int compareTo(@NotNull final Budget budget) {\n\n        int result = getName().compareTo(budget.getName());\n\n        if (result == 0) {\n            result = getDescription().compareTo(budget.getDescription());\n        }\n\n        if (result == 0) {\n            return getUuid().compareTo(budget.getUuid());\n        }\n\n        return result;\n    }\n\n    @Override\n    public boolean equals(final Object other) {\n        return this == other || other instanceof Budget && getUuid().equals(((Budget) other).getUuid());\n    }\n\n    /**\n     * Returns the global display period for this budget.\n     *\n     * @return The period for this budget\n     */\n    public Period getBudgetPeriod() {\n        return budgetPeriod;\n    }\n\n    /**\n     * Sets the global display period for this budget.\n     *\n     * @param budgetPeriod The budget period\n     */\n    public void setBudgetPeriod(final Period budgetPeriod) {\n        this.budgetPeriod = Objects.requireNonNull(budgetPeriod);\n    }\n\n    /**\n     * Returns a clone of this {@code Budget}.\n     *\n     * @return clone\n     * @throws java.lang.CloneNotSupportedException thrown if cloning error occurs\n     */\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n\n        Budget budget = (Budget) super.clone();\n\n        budget.setDescription(getDescription());\n        budget.setName(getName() + \"(\" + ResourceUtils.getString(\"Word.Copy\") + \")\");\n\n        budget.accountGoals = new HashMap<>();\n\n        for (final Map.Entry<String, BudgetGoal> entry : accountGoals.entrySet()) {\n            budget.accountGoals.put(entry.getKey(), (BudgetGoal) entry.getValue().clone());\n        }\n\n        budget.setBudgetPeriod(getBudgetPeriod());\n\n        return budget;\n    }\n\n    /**\n     * Returns true if asset accounts are included in the budget.\n     *\n     * @return the assetAccountsIncluded\n     */\n    public boolean areAssetAccountsIncluded() {\n        return assetAccountsIncluded;\n    }\n\n    /**\n     * Controls inclusion of asset accounts in the budget.\n     *\n     * @param assetAccountsIncluded the assetAccountsIncluded to set\n     */\n    public void setAssetAccountsIncluded(final boolean assetAccountsIncluded) {\n        this.assetAccountsIncluded = assetAccountsIncluded;\n    }\n\n    /**\n     * Returns true if income accounts are included in the budget.\n     *\n     * @return the incomeAccountsIncluded\n     */\n    public boolean areIncomeAccountsIncluded() {\n        return incomeAccountsIncluded;\n    }\n\n    /**\n     * Controls inclusion of income accounts in the budget.\n     *\n     * @param incomeAccountsIncluded the incomeAccountsIncluded to set\n     */\n    public void setIncomeAccountsIncluded(final boolean incomeAccountsIncluded) {\n        this.incomeAccountsIncluded = incomeAccountsIncluded;\n    }\n\n    /**\n     * Returns true if expense accounts are included in the budget.\n     *\n     * @return the expenseAccountsIncluded\n     */\n    public boolean areExpenseAccountsIncluded() {\n        return expenseAccountsIncluded;\n    }\n\n    /**\n     * Controls inclusion of expense accounts in the budget.\n     *\n     * @param expenseAccountsIncluded the expenseAccountsIncluded to set\n     */\n    public void setExpenseAccountsIncluded(final boolean expenseAccountsIncluded) {\n        this.expenseAccountsIncluded = expenseAccountsIncluded;\n    }\n\n    /**\n     * Returns true if liability accounts are included in the budget.\n     *\n     * @return the liabilityAccountsIncluded\n     */\n    public boolean areLiabilityAccountsIncluded() {\n        return liabilityAccountsIncluded;\n    }\n\n    /**\n     * Controls inclusion of liability accounts in the budget.\n     *\n     * @param liabilityAccountsIncluded the liabilityAccountsIncluded to set\n     */\n    public void setLiabilityAccountsIncluded(final boolean liabilityAccountsIncluded) {\n        this.liabilityAccountsIncluded = liabilityAccountsIncluded;\n    }\n\n    /**\n     * Overridden to return the name of the budget for convenience.\n     *\n     * @return name of the budget\n     */\n    @Override\n    public String toString() {\n        return getName();\n    }\n\n    /**\n     * Scale at which reported values are rounded to\n     *\n     * @return reporting scale for budget values\n     */\n    public byte getRoundingScale() {\n        return roundingScale;\n    }\n\n    /**\n     * Sets the reporting scale of budget values\n     *\n     * @param scale new scale value\n     */\n    public void setRoundingScale(final byte scale) {\n        this.roundingScale = scale;\n    }\n\n    /**\n     * Configured rounding mode for the budget\n     *\n     * @return returns the rounding mode\n     */\n    @NotNull\n    public RoundingMode getRoundingMode() {\n        return roundingMode;\n    }\n\n    /**\n     * Sets the rounding mode\n     *\n     * @param roundingMethod new rounding mode\n     */\n    public void setRoundingMode(@NotNull final RoundingMode roundingMethod) {\n        Objects.requireNonNull(roundingMethod);\n\n        this.roundingMode = roundingMethod;\n    }\n\n    /**\n     * Returns the starting month of the budget\n     * @since 3.1\n     */\n    public Month getStartMonth() {\n        return startMonth;\n    }\n\n    /**\n     * Sets the starting month of the budget\n     * @since 3.1\n     */\n    public void setStartMonth(Month startMonth) {\n        this.startMonth = startMonth;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/BudgetFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.time.Period;\n\n/**\n * Budget Factory for automatic generation of Budgets.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetFactory {\n\n    /**\n     * Utility class, no public constructor.\n     */\n    private BudgetFactory() {\n    }\n\n    public static Budget buildAverageBudget(final Period budgetPeriod, final String name, final boolean round) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Budget budget = new Budget();\n        budget.setName(name);\n        budget.setBudgetPeriod(budgetPeriod);\n\n        int year = LocalDate.now().getYear() - 1;\n\n        final List<BudgetPeriodDescriptor> descriptors\n                = BudgetPeriodDescriptorFactory.getDescriptors(year, budget.getStartMonth(), budgetPeriod);\n\n        final List<Account> accounts = new ArrayList<>();\n        accounts.addAll(engine.getIncomeAccountList());\n        accounts.addAll(engine.getExpenseAccountList());\n\n        for (final Account account : accounts) {\n            budget.setBudgetGoal(account, buildAverageBudgetGoal(account, descriptors, round));\n        }\n\n        return budget;\n    }\n\n    public static BudgetGoal buildAverageBudgetGoal(final Account account,\n                                                    final List<BudgetPeriodDescriptor> descriptors,\n                                                    final boolean round) {\n        BudgetGoal goal = new BudgetGoal();\n\n        goal.setBudgetPeriod(descriptors.get(0).getBudgetPeriod());\n\n        for (final BudgetPeriodDescriptor descriptor : descriptors) {\n            BigDecimal amount = account.getBalance(descriptor.getStartDate(), descriptor.getEndDate());\n\n            if (account.getAccountType() == AccountType.INCOME) {\n                amount = amount.negate();\n            }\n\n            if (round) {\n                if (account.getAccountType() == AccountType.INCOME) {\n                    amount = amount.setScale(0, RoundingMode.DOWN);\n                } else {\n                    amount = amount.setScale(0, RoundingMode.UP);\n                }\n            }\n\n            goal.setGoal(descriptor.getStartPeriod(), descriptor.getEndPeriod(), amount, descriptor.getStartDate().isLeapYear());\n        }\n\n        return goal;\n    }\n\n    /**\n     * Creates a {@code BudgetGoal} with an alternating pattern.\n     *\n     * @param baseBudgetGoal {@code BudgetGoal} to clone\n     * @param descriptors descriptors to use\n     * @param pattern Pattern to use\n     * @param startRow starting row, 0 based index is assumed\n     * @param endRow ending row, 0 based index is assumed\n     * @param amount amount to use\n     * @return new {@code BudgetGoal}\n     */\n    public static BudgetGoal buildBudgetGoal(final BudgetGoal baseBudgetGoal,\n                                             final List<BudgetPeriodDescriptor> descriptors,\n                                             final Pattern pattern, final int startRow, final int endRow,\n                                             final BigDecimal amount) {\n        BudgetGoal goal;\n\n        try {\n            goal = (BudgetGoal)baseBudgetGoal.clone();\n        } catch (CloneNotSupportedException e) {\n            Logger.getLogger(BudgetFactory.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            goal = new BudgetGoal();\n        }\n\n        goal.setBudgetPeriod(descriptors.get(0).getBudgetPeriod());\n\n        //System.out.println(\"Pattern: \" + pattern.getIncrement());\n\n        for (int i = startRow; i <= endRow; i += pattern.getIncrement()) {\n\n            //System.out.println(i);\n\n            final BudgetPeriodDescriptor descriptor = descriptors.get(i);\n\n            goal.setBudgetPeriod(descriptor.getBudgetPeriod());\n            goal.setGoal(descriptor.getStartPeriod(), descriptor.getEndPeriod(), amount, descriptor.getStartDate().isLeapYear());\n        }\n\n        return goal;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/BudgetGoal.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.time.Period;\n\nimport javax.persistence.Column;\nimport javax.persistence.ElementCollection;\nimport javax.persistence.Entity;\nimport javax.persistence.EnumType;\nimport javax.persistence.Enumerated;\nimport javax.persistence.FetchType;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.OrderColumn;\nimport javax.persistence.SequenceGenerator;\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Budget Goal Object\n * <p>\n * 366 days per year are assumed and static for goals. The 366th day will not be used if not a leap year\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class BudgetGoal implements Cloneable, Serializable {\n\n    @SuppressWarnings(\"unused\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    public long id;\n\n    /**\n     * 366 days per year.\n     */\n    public static final int PERIODS = 366;\n\n    // cache the hash code\n    private transient int hash;\n\n    @SuppressWarnings(\"UnusedAssignment\")\n    @ElementCollection(fetch = FetchType.EAGER)\n    @OrderColumn(name = \"INDEX\")\n    private List<BigDecimal> budgetGoals = new ArrayList<>();\n\n    @Enumerated(EnumType.STRING)\n    @Column(name = \"BUDGETPERIOD\")\n    private Period budgetPeriod = Period.MONTHLY;\n\n    public BudgetGoal() {\n        budgetGoals = new ArrayList<>(Collections.nCopies(PERIODS, BigDecimal.ZERO));\n    }\n\n    public final BigDecimal[] getGoals() {\n        return budgetGoals.toArray(BigDecimal[]::new);\n    }\n\n    public final void setGoals(final BigDecimal[] goals) {\n        Objects.requireNonNull(goals);\n\n        if (goals.length != PERIODS) {\n            throw new IllegalArgumentException(\"goals must be \" + PERIODS + \" in length\");\n        }\n\n        for (int i = 0; i < goals.length; i++) {\n            if (goals[i] == null) {\n                throw new IllegalArgumentException(\"goals [\" + i + \"] may not be null\");\n            }\n        }\n\n        for (int i = 0; i < goals.length; i++) {\n            budgetGoals.set(i, goals[i]);\n        }\n    }\n\n    /**\n     * Returns the entry / display period for this budget goal.\n     *\n     * @return the BudgetPeriod\n     */\n    public Period getBudgetPeriod() {\n        return budgetPeriod;\n    }\n\n    /**\n     * Sets the global entry / display period for this budget goal.\n     *\n     * @param budgetPeriod The budget period\n     */\n    public void setBudgetPeriod(final Period budgetPeriod) {\n        this.budgetPeriod = Objects.requireNonNull(budgetPeriod);\n    }\n\n    public void setGoal(final int startPeriod, final int endPeriod, final BigDecimal amount, final boolean leapYear) {\n        if (startPeriod <= endPeriod) {\n\n            final BigDecimal divisor = new BigDecimal(endPeriod - startPeriod + 1);\n            final BigDecimal portion = amount.divide(divisor, MathConstants.budgetMathContext);\n\n            for (int i = startPeriod; i <= endPeriod && i < BudgetGoal.PERIODS; i++) {\n                budgetGoals.set(i, portion);\n            }\n        } else {    // wrap around the array, need to handle a leap year\n            final BigDecimal divisor = new BigDecimal(BudgetGoal.PERIODS - startPeriod + endPeriod - (leapYear ? 1 : 0));\n            final BigDecimal portion = amount.divide(divisor, MathConstants.budgetMathContext);\n\n            //System.out.println(\"divisor: \" + divisor+ \", start: \" + startPeriod + \", end: \" + endPeriod + \", leapYear: \" + leapYear);\n\n            for (int i = startPeriod; i < BudgetGoal.PERIODS - (leapYear ? 0 : 1); i++) {\n                budgetGoals.set(i, portion);\n            }\n\n            for (int i = 0; i <= endPeriod && i < BudgetGoal.PERIODS; i++) {\n                budgetGoals.set(i, portion);\n            }\n        }\n    }\n\n    public BigDecimal getGoal(final int startPeriod, final int endPeriod, final boolean leapYear) {\n        BigDecimal amount = BigDecimal.ZERO;\n\n\n        if (startPeriod <= endPeriod) {\n            // clip to the max number of periods... some locale calendars behave differently\n            for (int i = startPeriod; i <= endPeriod && i < BudgetGoal.PERIODS; i++) {\n                amount = amount.add(budgetGoals.get(i));\n            }\n        } else {    // wrap around the array, need to handle a leap year\n            //int days = 0;\n\n            for (int i = startPeriod; i < BudgetGoal.PERIODS - (leapYear ? 0 : 1); i++) {\n                //days++;\n                amount = amount.add(budgetGoals.get(i));\n            }\n\n            for (int i = 0; i <= endPeriod && i < BudgetGoal.PERIODS; i++) {\n                //days++;\n                amount = amount.add(budgetGoals.get(i));\n            }\n\n            //System.out.println(\"days: \" + days+ \", start: \" + startPeriod + \", end: \" + endPeriod + \", leapYear: \" + leapYear);\n\n        }\n\n        return amount;\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p>\n     * A deep copy of the goals is performed\n     */\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n        final BudgetGoal goal = (BudgetGoal) super.clone();\n\n        goal.id = 0;    // clones id must be reset for JPA\n\n        // deep copy\n        goal.budgetGoals = new ArrayList<>(Collections.nCopies(PERIODS, BigDecimal.ZERO));\n\n        for (int i = 0; i < PERIODS; i++) {\n            goal.budgetGoals.set(i, budgetGoals.get(i));\n        }\n\n        return goal;\n    }\n\n    @Override\n    public int hashCode() {\n        int h = hash;\n        if (h == 0) {\n            final int prime = 31;\n            h = 1;\n            h = prime * h + budgetPeriod.hashCode();\n            h = prime * h + budgetGoals.hashCode();\n\n            hash = h;\n        }\n        return h;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n\n        if (obj == null) {\n            return false;\n        }\n\n        if (!(obj instanceof BudgetGoal)) {\n            return false;\n        }\n\n        final BudgetGoal other = (BudgetGoal) obj;\n\n        return budgetPeriod == other.budgetPeriod && budgetGoals.equals(other.budgetGoals);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/BudgetPeriodDescriptor.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport java.time.LocalDate;\nimport java.util.Objects;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.time.Period;\nimport jgnash.util.NotNull;\n\n/**\n * Immutable descriptor for a budget period.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetPeriodDescriptor implements Comparable<BudgetPeriodDescriptor> {\n\n    private int hash = 0;\n\n    /**\n     * The starting period (Day of the year).\n     */\n    private int startPeriod;\n\n    private final int endPeriod;\n\n    private final LocalDate startDate;\n\n    private final LocalDate endDate;\n\n    private final String periodDescription;\n\n    private final Period budgetPeriod;\n\n    /**\n     *\n     * @param periodStartDate the user specified start of the budget (fiscal year)\n     * @param periodEndDate the starting date for the period\n     * @param budgetPeriod the period for the descriptor\n     */\n    BudgetPeriodDescriptor(final LocalDate periodStartDate, final LocalDate periodEndDate, final Period budgetPeriod) {\n        Objects.requireNonNull(budgetPeriod);\n        Objects.requireNonNull(periodStartDate);\n        Objects.requireNonNull(periodEndDate);\n\n        this.budgetPeriod = budgetPeriod;\n\n        this.startDate = periodStartDate;\n        this.endDate = periodEndDate;\n\n        startPeriod = this.startDate.getDayOfYear() - 1;\n        endPeriod = this.endDate.getDayOfYear() - 1;\n\n\n        // correctly handle the roll over of the index\n        if (periodEndDate.getYear() > periodStartDate.getYear()) {\n            int startYearDays = periodStartDate.lengthOfYear();\n            int endYearDays = periodEndDate.lengthOfYear();\n\n            startPeriod += startYearDays - endYearDays + 1; // shift for a leap year/day\n        }\n\n        switch (budgetPeriod) {\n            case DAILY:\n                periodDescription = ResourceUtils.getString(\"Pattern.NumericDate\", DateUtils.asDate(startDate));\n                break;\n            case WEEKLY:\n                periodDescription = ResourceUtils.getString(\"Pattern.WeekOfYear\",\n                        DateUtils.getWeekOfTheYear(startDate), startDate.getYear());\n                break;\n            case BI_WEEKLY:\n                periodDescription = ResourceUtils.getString(\"Pattern.DateRangeShort\", DateUtils.asDate(startDate),\n                        DateUtils.asDate(endDate));\n                break;\n            case MONTHLY:\n                periodDescription = ResourceUtils.getString(\"Pattern.MonthOfYear\", DateUtils.asDate(startDate));\n                break;\n            case QUARTERLY:\n                periodDescription = ResourceUtils.getString(\"Pattern.QuarterOfYear\",\n                        DateUtils.getQuarterNumber(startDate), startDate.getYear());\n                break;\n            case YEARLY:\n                if (periodEndDate.getYear() == periodStartDate.getYear()) {\n                    periodDescription = Integer.toString(periodStartDate.getYear());\n                } else {\n                    periodDescription = ResourceUtils.getString(\"Pattern.DateRangeShort\",\n                            DateUtils.asDate(startDate), DateUtils.asDate(endDate));\n                }\n                break;\n            default:\n                periodDescription = \"\";\n        }\n    }\n\n    public int getStartPeriod() {\n        return startPeriod;\n    }\n\n    public int getEndPeriod() {\n        return endPeriod;\n    }\n\n    public LocalDate getStartDate() {\n        return startDate;\n    }\n\n    public LocalDate getEndDate() {\n        return endDate;\n    }\n\n    public String getPeriodDescription() {\n        return periodDescription;\n    }\n\n    Period getBudgetPeriod() {\n        return budgetPeriod;\n    }\n\n    /**\n     * Determines if the specified date lies within or inclusive of this descriptor period.\n     *\n     * @param date date to check\n     * @return true if between or inclusive\n     */\n    public boolean isBetween(final LocalDate date) {\n        return DateUtils.after(date, startDate) && DateUtils.before(date, endDate);\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"BudgetPeriodDescriptor [startDate=%tD, endDate=%tD, periodDescription=%s, budgetPeriod=%s, startPeriod=%3d, endPeriod=%3d]\",\n                DateUtils.asDate(startDate), DateUtils.asDate(endDate), periodDescription, budgetPeriod, startPeriod, endPeriod);\n    }\n\n    @Override\n    public int hashCode() {\n        int h = hash;\n        if (h == 0) {\n            final int prime = 31;\n\n            h = 1;\n            h = prime * h + budgetPeriod.hashCode();\n            h = prime * h + startPeriod;\n            h = prime * h + endPeriod;\n            hash = h;\n        }\n        return h;\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (this == obj) {\n            return true;\n        }\n\n        if (obj == null) {\n            return false;\n        }\n\n        if (!(obj instanceof BudgetPeriodDescriptor)) {\n            return false;\n        }\n\n        BudgetPeriodDescriptor other = (BudgetPeriodDescriptor) obj;\n\n        return budgetPeriod == other.budgetPeriod && startPeriod == other.startPeriod;\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p>\n     * Compares by the start date\n     */\n    @Override\n    public int compareTo(@NotNull final BudgetPeriodDescriptor that) {\n        return this.startDate.compareTo(that.getStartDate());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/BudgetPeriodDescriptorFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\nimport jgnash.time.DateUtils;\nimport jgnash.time.Period;\n\nimport org.apache.commons.collections4.map.ReferenceMap;\n\n/**\n * Factory class for generating descriptors.\n *\n * @author Craig Cavanaugh\n */\npublic final class BudgetPeriodDescriptorFactory {\n\n    private static final Map<String, List<BudgetPeriodDescriptor>> cache = new ReferenceMap<>();\n\n    private static final ReadWriteLock rwl = new ReentrantReadWriteLock(false);\n\n    private BudgetPeriodDescriptorFactory() {\n    }\n\n    public static List<BudgetPeriodDescriptor> getDescriptors(final int budgetYear, final Month startingMonth, final Period budgetPeriod) {\n\n        final String cacheKey = budgetYear + budgetPeriod.name() + startingMonth.ordinal();\n\n        List<BudgetPeriodDescriptor> descriptors;\n\n        rwl.readLock().lock();\n        try {\n            descriptors = cache.get(cacheKey);\n        } finally {\n            rwl.readLock().unlock();\n        }\n\n        // build the descriptor List if not cached\n        if (descriptors == null) {\n\n            LocalDate[] dates = new LocalDate[0];\n\n            switch (budgetPeriod) {\n                case DAILY:\n                    dates = DateUtils.getAllDays(startingMonth, budgetYear, DateUtils.getDaysInYear(budgetYear) + 1);\n                    break;\n                case WEEKLY:\n                    dates = DateUtils.getFirstDayWeekly(startingMonth, budgetYear, 52 + 1);\n                    break;\n                case BI_WEEKLY:\n                    dates = DateUtils.getFirstDayBiWeekly(startingMonth, budgetYear, 26 + 1);\n                    break;\n                case MONTHLY:\n                    dates = DateUtils.getFirstDayMonthly(startingMonth, budgetYear, 12 + 1);\n                    break;\n                case QUARTERLY:\n                    dates = DateUtils.getFirstDayQuarterly(startingMonth, budgetYear, 4 + 1);\n                    break;\n                case YEARLY:\n                    dates = DateUtils.getFirstDayWeekly(startingMonth, budgetYear, 2, 52);\n                    break;\n            }\n\n            descriptors = new ArrayList<>(dates.length);\n\n            for (int i = 0; i < dates.length - 1; i++) {\n                descriptors.add(new BudgetPeriodDescriptor(dates[i], dates[i + 1].minusDays(1), budgetPeriod));\n            }\n\n            // Debugging info\n            /*System.out.println(\"Descriptor list length: \" + descriptors.size());\n            for (final BudgetPeriodDescriptor descriptor : descriptors) {\n                System.out.println(descriptor.toString());\n            }*/\n\n            rwl.writeLock().lock();\n            try {\n                cache.put(cacheKey, descriptors);\n            } finally {\n                rwl.writeLock().unlock();\n            }\n        }\n\n        return descriptors;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/BudgetPeriodResults.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport java.math.BigDecimal;\n\n/**\n * A simple wrapper for calculated budget results.\n *\n * @author Craig Cavanaugh\n *\n */\npublic class BudgetPeriodResults {\n\n    private BigDecimal budgeted = BigDecimal.ZERO;\n\n    private BigDecimal change = BigDecimal.ZERO;\n\n    private BigDecimal remaining = BigDecimal.ZERO;\n\n    public BigDecimal getBudgeted() {\n        return budgeted;\n    }\n\n    public void setBudgeted(final BigDecimal budgeted) {\n        this.budgeted = budgeted;\n    }\n\n    public BigDecimal getChange() {\n        return change;\n    }\n\n    public void setChange(final BigDecimal change) {\n        this.change = change;\n    }\n\n    public BigDecimal getRemaining() {\n        return remaining;\n    }\n\n    public void setRemaining(final BigDecimal balance) {\n        this.remaining = balance;\n    }\n\n    @Override\n    public String toString() {\n        return String.format(\"Budgeted: %f Change: %f Remaining: %f\", budgeted, change, remaining);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/BudgetResultsModel.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.budget;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.EnumMap;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.stream.Collectors;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.engine.message.MessageProxy;\n\n/**\n * Model for budget results.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetResultsModel implements MessageListener {\n\n    private Set<Account> accounts = new HashSet<>();\n\n    private final Budget budget;\n\n    private final CurrencyNode baseCurrency;\n\n    private List<AccountGroup> accountGroupList;\n\n    private final List<BudgetPeriodDescriptor> descriptorList;\n\n    private final ReentrantReadWriteLock accountLock = new ReentrantReadWriteLock();\n\n    private final ReentrantLock cacheLock = new ReentrantLock();\n\n    private final Map<Account, BudgetPeriodResults> accountResultsCache;\n\n    private final Map<AccountGroup, BudgetPeriodResults> accountGroupResultsCache;\n\n    private final Map<BudgetPeriodDescriptor, Map<Account, BudgetPeriodResults>> descriptorAccountResultsCache;\n\n    private final Map<BudgetPeriodDescriptor, Map<AccountGroup, BudgetPeriodResults>> descriptorAccountGroupResultsCache;\n\n    private final boolean useRunningTotals;\n\n    /**\n     * Message proxy.\n     */\n    private final MessageProxy proxy = new MessageProxy();\n\n    public BudgetResultsModel(final Budget budget, final int year, final CurrencyNode baseCurrency, final boolean useRunningTotals) {\n        this.budget = budget;\n        this.descriptorList = BudgetPeriodDescriptorFactory.getDescriptors(year, budget.getStartMonth(), budget.getBudgetPeriod());\n\n        this.baseCurrency = baseCurrency;\n        this.useRunningTotals = useRunningTotals;\n\n        accountResultsCache = new HashMap<>();\n        accountGroupResultsCache = new EnumMap<>(AccountGroup.class);\n        descriptorAccountResultsCache = new HashMap<>();\n        descriptorAccountGroupResultsCache = new HashMap<>();\n\n        loadAccounts();\n        loadAccountGroups();\n\n        registerListeners();\n    }\n\n    public Budget getBudget() {\n        return budget;\n    }\n\n    public CurrencyNode getBaseCurrency() {\n        return baseCurrency;\n    }\n\n    /**\n     * Returns the depth of the account relative to topmost account in the\n     * budget hierarchy.\n     *\n     * @param account Account to get depth for\n     * @return depth depth relative to accounts to be shown in the budget\n     * @see Account#getDepth()\n     */\n    public int getDepth(final Account account) {\n\n        int depth = 0;\n\n        Account parent = account.getParent();\n\n        while (parent != null) {\n            if (accounts.contains(parent)) {\n                depth++;\n            }\n            parent = parent.getParent();\n        }\n\n        return depth;\n    }\n\n    private boolean areParentsIncluded(final Account account) {\n        boolean result = true;\n\n        Account parent = account.getParent();\n\n        if (parent != null && !(parent instanceof RootAccount)) {\n            if (!includeAccount(parent)) {\n                result = false;\n            } else {\n                result = areParentsIncluded(parent);\n            }\n        }\n\n        return result;\n    }\n\n    public List<BudgetPeriodDescriptor> getDescriptorList() {\n        return descriptorList;\n    }\n\n    private void registerListeners() {\n        MessageBus.getInstance().registerListener(this, MessageChannel.ACCOUNT, MessageChannel.BUDGET, MessageChannel.SYSTEM, MessageChannel.TRANSACTION);\n    }\n\n    private void unregisterListeners() {\n        MessageBus.getInstance().unregisterListener(this, MessageChannel.ACCOUNT, MessageChannel.BUDGET, MessageChannel.SYSTEM, MessageChannel.TRANSACTION);\n    }\n\n    public synchronized void addMessageListener(final MessageListener messageListener) {\n        proxy.addMessageListener(messageListener);\n    }\n\n    public synchronized void removeMessageListener(final MessageListener messageListener) {\n        proxy.removeMessageListener(messageListener);\n    }\n\n    /**\n     * Determines if the account will be added to the model.\n     *\n     * @param account Account to test\n     * @return true if it should be added\n     */\n    public boolean includeAccount(final Account account) {\n\n        boolean result = false;\n\n        // account must be visible and not marked for exclusion from a budget\n        if (account.isVisible() && !account.isExcludedFromBudget()) {\n            if (account.memberOf(AccountGroup.INCOME) && budget.areIncomeAccountsIncluded()) {\n                result = true;\n            } else if (account.memberOf(AccountGroup.EXPENSE) && budget.areExpenseAccountsIncluded()) {\n                result = true;\n            } else if (account.memberOf(AccountGroup.ASSET) && budget.areAssetAccountsIncluded()) {\n                result = true;\n            } else if (account.memberOf(AccountGroup.LIABILITY) && budget.areLiabilityAccountsIncluded()) {\n                result = true;\n            }\n        }\n\n        if (result) {\n            result = areParentsIncluded(account);\n        }\n\n        return result;\n    }\n\n    private void loadAccounts() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        Set<Account> accountSet = engine.getAccountList().stream()\n                .filter(this::includeAccount).collect(Collectors.toSet());\n\n        accountLock.writeLock().lock();\n\n        try {\n            accounts = accountSet;\n        } finally {\n            accountLock.writeLock().unlock();\n        }\n    }\n\n    private void loadAccountGroups() {\n        accountLock.writeLock().lock();\n\n        try {\n            final EnumSet<AccountGroup> accountSet = EnumSet.noneOf(AccountGroup.class);\n\n            accountSet.addAll(accounts.stream()\n                    .map(account -> account.getAccountType().getAccountGroup()).collect(Collectors.toList()));\n\n            // create a list and sort\n            List<AccountGroup> groups = new ArrayList<>(accountSet);\n\n            // Set an explicit sort order\n            groups.sort(new Comparators.ExplicitComparator<>(AccountGroup.INCOME,\n                    AccountGroup.EXPENSE, AccountGroup.ASSET, AccountGroup.LIABILITY));\n\n            accountGroupList = groups;\n        } finally {\n            accountLock.writeLock().unlock();\n        }\n    }\n\n    public List<AccountGroup> getAccountGroupList() {\n        accountLock.readLock().lock();\n\n        try {\n            return accountGroupList;\n        } finally {\n            accountLock.readLock().unlock();\n        }\n    }\n\n    public final Set<Account> getAccounts() {\n        accountLock.readLock().lock();\n\n        try {\n            // return a defensive copy\n            return new HashSet<>(accounts);\n        } finally {\n            accountLock.readLock().unlock();\n        }\n    }\n\n    private Set<Account> getAccounts(final AccountGroup group) {\n        accountLock.readLock().lock();\n\n        try {\n            return accounts.stream().filter(account -> account.memberOf(group)).collect(Collectors.toSet());\n        } finally {\n            accountLock.readLock().unlock();\n        }\n    }\n\n    private void clear(final BudgetPeriodDescriptor descriptor, final Account account) {\n        cacheLock.lock();\n\n        try {\n            final Map<Account, BudgetPeriodResults> resultsMap = descriptorAccountResultsCache.get(descriptor);\n\n            if (resultsMap != null) {\n                resultsMap.remove(account);\n            }\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    private void clear(final BudgetPeriodDescriptor descriptor, final AccountGroup group) {\n        cacheLock.lock();\n\n        try {\n            final Map<AccountGroup, BudgetPeriodResults> resultsMap = descriptorAccountGroupResultsCache.get(descriptor);\n\n            if (resultsMap != null) {\n                resultsMap.remove(group);\n            }\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    private void clear(final Account account) {\n        cacheLock.lock();\n\n        try {\n            accountResultsCache.remove(account);\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    private void clear(final AccountGroup accountGroup) {\n        cacheLock.lock();\n\n        try {\n            final BudgetPeriodResults results = accountGroupResultsCache.get(accountGroup);\n\n            if (results != null) {\n                accountGroupResultsCache.remove(accountGroup);\n            }\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    private void clearCached() {\n        cacheLock.lock();\n\n        try {\n            accountResultsCache.clear();\n            accountGroupResultsCache.clear();\n            descriptorAccountResultsCache.clear();\n            descriptorAccountGroupResultsCache.clear();\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    /**\n     * Gets results by descriptor and account (per account results).\n     *\n     * @param descriptor BudgetPeriodDescriptor descriptor\n     * @param account    Account\n     * @return cached or newly created BudgetPeriodResults\n     */\n    public BudgetPeriodResults getResults(final BudgetPeriodDescriptor descriptor, final Account account) {\n        cacheLock.lock();\n\n        try {\n            final Map<Account, BudgetPeriodResults> resultsMap\n                    = descriptorAccountResultsCache.computeIfAbsent(descriptor, k -> new HashMap<>());\n\n            return resultsMap.computeIfAbsent(account, k -> buildAccountResults(descriptor, account, true));\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    /**\n     * Gets summary result by descriptor and account group (column summary by\n     * AccountGroup).\n     *\n     * @param descriptor BudgetPeriodDescriptor for summary\n     * @param group      AccountGroup for summary\n     * @return summary results\n     */\n    public BudgetPeriodResults getResults(final BudgetPeriodDescriptor descriptor, final AccountGroup group) {\n        cacheLock.lock();\n\n        try {\n            final Map<AccountGroup, BudgetPeriodResults> resultsMap = descriptorAccountGroupResultsCache\n                    .computeIfAbsent(descriptor, k -> new EnumMap<>(AccountGroup.class));\n\n            return resultsMap.computeIfAbsent(group, k -> buildResults(descriptor, group));\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    /**\n     * Gets summary result by account (row summary).\n     *\n     * @param account Account for summary\n     * @return summary results\n     */\n    public BudgetPeriodResults getResults(final Account account) {\n        cacheLock.lock();\n\n        try {\n            return accountResultsCache.computeIfAbsent(account, k -> buildResults(account));\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n    /**\n     * Gets summary result by account group (corner summary).\n     *\n     * @param accountGroup AccountGroup for summary\n     * @return summary results\n     */\n    public BudgetPeriodResults getResults(final AccountGroup accountGroup) {\n        cacheLock.lock();\n\n        try {\n            return accountGroupResultsCache.computeIfAbsent(accountGroup, k -> buildResults(accountGroup));\n        } finally {\n            cacheLock.unlock();\n        }\n    }\n\n\n    private BudgetPeriodResults buildAccountResults(final BudgetPeriodDescriptor descriptor, final Account account,\n                                                    final boolean includeBaseAccountResults) {\n        final BudgetPeriodResults results = new BudgetPeriodResults();\n\n        accountLock.readLock().lock();\n\n        try {\n            // calculate this account's results\n            if (accounts.contains(account)) {\n                final BudgetGoal goal = budget.getBudgetGoal(account);\n\n                results.setBudgeted(goal.getGoal(descriptor.getStartPeriod(), descriptor.getEndPeriod(),\n                        descriptor.getStartDate().isLeapYear()));\n\n                // calculate the change and remaining amount for the budget\n                if (account.getAccountType() == AccountType.INCOME) {\n                    results.setChange(account.getBalance(descriptor.getStartDate(), descriptor.getEndDate()).negate());\n                    results.setRemaining(results.getChange().subtract(results.getBudgeted()));\n                } else {\n                    results.setChange(account.getBalance(descriptor.getStartDate(), descriptor.getEndDate()));\n                    results.setRemaining(results.getBudgeted().subtract(results.getChange()));\n                }\n\n                final int index = descriptorList.indexOf(descriptor);\n\n                // per account running total\n                if (useRunningTotals && index > 0 && includeBaseAccountResults) {\n                    final BudgetPeriodResults priorResults = getResults(descriptorList.get(index - 1), account);\n\n                    results.setBudgeted(results.getBudgeted().add(priorResults.getBudgeted()));\n\n                    results.setChange(results.getChange().add(priorResults.getChange()));\n                    results.setRemaining(results.getRemaining().add(priorResults.getRemaining()));\n                }\n            }\n\n            // recursive decent to add child account results and handle exchange rates\n            for (final Account child : account.getChildren(Comparators.getAccountByCode())) {\n                final BudgetPeriodResults childResults = buildAccountResults(descriptor, child, false);\n\n                final BigDecimal exchangeRate = child.getCurrencyNode().getExchangeRate(account.getCurrencyNode());\n\n                // reverse sign if the parent account is an income account but the child is not, or vice versa\n                final BigDecimal sign = ((account.getAccountType() == AccountType.INCOME) !=\n                        (child.getAccountType() == AccountType.INCOME)) ? BigDecimal.ONE.negate() : BigDecimal.ONE;\n\n                results.setChange(results.getChange().add(childResults.getChange().multiply(exchangeRate).multiply(sign)));\n                results.setBudgeted(results.getBudgeted().add(childResults.getBudgeted().multiply(exchangeRate).multiply(sign)));\n                results.setRemaining(results.getRemaining().add(childResults.getRemaining().multiply(exchangeRate)));\n            }\n\n        } finally {\n            accountLock.readLock().unlock();\n        }\n\n        // rescale the results\n        results.setChange(results.getChange().setScale(budget.getRoundingScale(), budget.getRoundingMode()));\n        results.setBudgeted(results.getBudgeted().setScale(budget.getRoundingScale(), budget.getRoundingMode()));\n        results.setRemaining(results.getRemaining().setScale(budget.getRoundingScale(), budget.getRoundingMode()));\n\n        return results;\n    }\n\n    private BudgetPeriodResults buildResults(final BudgetPeriodDescriptor descriptor, final AccountGroup group) {\n        BigDecimal remainingTotal = BigDecimal.ZERO;\n        BigDecimal totalChange = BigDecimal.ZERO;\n        BigDecimal totalBudgeted = BigDecimal.ZERO;\n\n        accountLock.readLock().lock();\n\n        try {\n            for (final Account account : getAccounts(group)) {\n\n                // only sum for the top level accounts within the budget model\n                // top level account if the parent is not included in the budget model\n                if (!accounts.contains(account.getParent())) {\n\n                    final BudgetPeriodResults periodResults = getResults(descriptor, account);\n\n                    final BigDecimal remaining = periodResults.getRemaining();\n                    remainingTotal = remainingTotal.add(remaining.multiply(baseCurrency.getExchangeRate(account.getCurrencyNode())));\n\n                    final BigDecimal change = periodResults.getChange();\n                    totalChange = totalChange.add(change.multiply(baseCurrency.getExchangeRate(account.getCurrencyNode())));\n\n                    final BigDecimal budgeted = periodResults.getBudgeted();\n                    totalBudgeted = totalBudgeted.add(budgeted.multiply(baseCurrency.getExchangeRate(account.getCurrencyNode())));\n                }\n            }\n        } finally {\n            accountLock.readLock().unlock();\n        }\n\n        final BudgetPeriodResults results = new BudgetPeriodResults();\n\n        if (useRunningTotals) {\n            final int index = descriptorList.indexOf(descriptor);\n            if (index > 0) {\n                final BudgetPeriodResults priorResults = getResults(descriptorList.get(index - 1), group);\n\n                results.setBudgeted(results.getBudgeted().add(priorResults.getBudgeted()));\n                results.setChange(results.getChange().add(priorResults.getChange()));\n                results.setRemaining(results.getRemaining().add(priorResults.getRemaining()));\n            }\n        }\n\n        // rescale the results\n        results.setBudgeted(totalBudgeted.setScale(budget.getRoundingScale(), budget.getRoundingMode()));\n        results.setRemaining(remainingTotal.setScale(budget.getRoundingScale(), budget.getRoundingMode()));\n        results.setChange(totalChange.setScale(budget.getRoundingScale(), budget.getRoundingMode()));\n\n        return results;\n    }\n\n    private BudgetPeriodResults buildResults(final Account account) {\n        BigDecimal change = BigDecimal.ZERO;\n        BigDecimal budgeted = BigDecimal.ZERO;\n        BigDecimal remaining = BigDecimal.ZERO;\n\n        accountLock.readLock().lock();\n\n        try {\n            if (useRunningTotals) { // just use the last descriptor\n                final BudgetPeriodResults periodResults\n                        = getResults(descriptorList.get(descriptorList.size() - 1), account);\n\n                change = periodResults.getChange();\n                budgeted = periodResults.getBudgeted();\n                remaining = periodResults.getRemaining();\n            } else {\n                for (final BudgetPeriodDescriptor descriptor : descriptorList) {\n                    final BudgetPeriodResults periodResults = getResults(descriptor, account);\n\n                    change = change.add(periodResults.getChange());\n                    budgeted = budgeted.add(periodResults.getBudgeted());\n                    remaining = remaining.add(periodResults.getRemaining());\n                }\n            }\n        } finally {\n            accountLock.readLock().unlock();\n        }\n\n        final BudgetPeriodResults results = new BudgetPeriodResults();\n\n        results.setChange(change);\n        results.setBudgeted(budgeted);\n        results.setRemaining(remaining);\n\n        return results;\n    }\n\n    private BudgetPeriodResults buildResults(final AccountGroup group) {\n        BigDecimal totalChange = BigDecimal.ZERO;\n        BigDecimal totalBudgeted = BigDecimal.ZERO;\n        BigDecimal totalRemaining = BigDecimal.ZERO;\n\n        accountLock.readLock().lock();\n\n        try {\n\n            if (useRunningTotals) {\n                final BudgetPeriodResults periodResults\n                        = getResults(descriptorList.get(descriptorList.size() - 1), group);\n\n                totalChange = periodResults.getChange();\n                totalBudgeted = periodResults.getBudgeted();\n                totalRemaining = periodResults.getRemaining();\n            } else {\n                for (final BudgetPeriodDescriptor descriptor : descriptorList) {\n                    final BudgetPeriodResults periodResults = getResults(descriptor, group);\n\n                    totalChange = totalChange.add(periodResults.getChange());\n                    totalBudgeted = totalBudgeted.add(periodResults.getBudgeted());\n                    totalRemaining = totalRemaining.add(periodResults.getRemaining());\n                }\n            }\n        } finally {\n            accountLock.readLock().unlock();\n        }\n\n        final BudgetPeriodResults results = new BudgetPeriodResults();\n\n        results.setChange(totalChange);\n        results.setBudgeted(totalBudgeted);\n        results.setRemaining(totalRemaining);\n\n        return results;\n    }\n\n    private void clearCached(final Account account) {\n        accountLock.readLock().lock();\n\n        try {\n            cacheLock.lock();\n\n            try {\n                // clear cached results\n                // could be mixed group tree\n                account.getAncestors().stream().filter(accounts::contains).forEach(ancestor -> {\n                    for (BudgetPeriodDescriptor descriptor : descriptorList) {\n                        clear(ancestor);\n                        clear(descriptor, ancestor);\n                        clear(ancestor.getAccountType().getAccountGroup()); // could be mixed group tree\n                        clear(descriptor, ancestor.getAccountType().getAccountGroup());\n                    }\n                });\n            } finally {\n                cacheLock.unlock();\n            }\n        } finally {\n            accountLock.readLock().unlock();\n        }\n    }\n\n    private void processAccountEvent(final Message message) {\n        Account account = message.getObject(MessageProperty.ACCOUNT);\n\n        switch (message.getEvent()) {\n            case ACCOUNT_ADD:\n                accounts.add(account);\n                clearCached(account);\n                break;\n            case ACCOUNT_REMOVE:\n                accounts.remove(account);\n                clearCached(account);\n                break;\n            case ACCOUNT_MODIFY:\n                loadAccounts(); // force a reload of accounts, structure is indeterminate\n                clearCached();  // indeterminate structure, so dump all cached results\n                break;\n            default:\n                break;\n        }\n\n        loadAccountGroups();    // force reload of account groups after accounts have changed\n    }\n\n    private void processBudgetEvent(final Message message) {\n        Budget messageBudget = message.getObject(MessageProperty.BUDGET);\n\n        if (budget.equals(messageBudget)) {\n            switch (message.getEvent()) {\n                case BUDGET_UPDATE:\n                    clearCached();\n                    break;\n                case BUDGET_GOAL_UPDATE:\n                    Account account = message.getObject(MessageProperty.ACCOUNT);\n                    clearCached(account);\n                    break;\n                case BUDGET_REMOVE:\n                    unregisterListeners();\n                    clearCached();\n                    break;\n                default:\n            }\n        }\n    }\n\n    private void processTransactionEvent(final Message message) {\n        final Transaction transaction = message.getObject(MessageProperty.TRANSACTION);\n\n        descriptorList.stream().filter(descriptor -> descriptor.isBetween(transaction.getLocalDate()))\n                .forEach(descriptor -> {\n                    final Set<Account> accountSet = new HashSet<>();\n\n                    for (Account account : transaction.getAccounts()) {\n                        accountSet.addAll(account.getAncestors());\n                    }\n\n                    accountSet.forEach(this::clearCached);\n                });\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        switch (message.getEvent()) {\n            case ACCOUNT_ADD:\n            case ACCOUNT_MODIFY:\n            case ACCOUNT_MODIFY_FAILED:\n                processAccountEvent(message);\n                break;\n            case BUDGET_UPDATE:\n            case BUDGET_GOAL_UPDATE:\n            case BUDGET_REMOVE:\n                processBudgetEvent(message);\n                break;\n            case TRANSACTION_ADD:\n            case TRANSACTION_REMOVE:\n                processTransactionEvent(message);\n                break;\n            case FILE_CLOSING:\n                unregisterListeners();\n                clearCached();\n                break;\n            default:\n        }\n\n        proxy.forwardMessage(message);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/budget/Pattern.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.engine.budget;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Pattern Enum.\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"unused\")\npublic enum Pattern {\n    EveryRow(ResourceUtils.getString(\"Sequence.EveryRow\")),\n    EveryOtherRow(ResourceUtils.getString(\"Sequence.EveryOtherRow\")),\n    EverySecondRow(ResourceUtils.getString(\"Sequence.EverySecondRow\")),\n    EveryThirdRow(ResourceUtils.getString(\"Sequence.EveryThirdRow\")),\n    EveryForthRow(ResourceUtils.getString(\"Sequence.EveryForthRow\")),\n    EveryFifthRow(ResourceUtils.getString(\"Sequence.EveryFifthRow\"));\n\n    private final transient String description;\n\n    Pattern(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n    public int getIncrement() {\n        return this.ordinal() + 1;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/concurrent/DistributedLockManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.concurrent;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandlerAdapter;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.util.CharsetUtil;\nimport io.netty.util.ReferenceCountUtil;\n\nimport java.text.MessageFormat;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.net.ConnectionFactory;\nimport jgnash.util.EncryptionManager;\nimport jgnash.util.NotNull;\n\n/**\n * Lock manager for distributed engine instances.\n *\n * @author Craig Cavanaugh\n */\npublic class DistributedLockManager implements LockManager {\n\n    private static final Logger logger = Logger.getLogger(DistributedLockManager.class.getName());\n\n    private final Map<String, DistributedReadWriteLock> lockMap = new ConcurrentHashMap<>();\n\n    private final Map<String, CountDownLatch> latchMap = new HashMap<>();\n\n    private final Lock latchLock = new ReentrantLock();\n\n    /**\n     * lock_action, lock_id, thread_id, lock_type.\n     */\n    private static final String PATTERN = \"{0},{1},{2},{3}\";\n\n    static final String UUID_PREFIX = \"UUID:\";\n\n    private NioEventLoopGroup eventLoopGroup;\n\n    private final int port;\n\n    private final String host;\n\n    private Channel channel;\n\n    private static final String EOL_DELIMITER = \"\\r\\n\";\n\n    private final ExecutorService executorService = Executors.newCachedThreadPool(new LockManagerThreadFactory());\n\n    private EncryptionManager encryptionManager = null;\n\n    /**\n     * Unique id to differentiate remote threads.\n     */\n    private static final String uuid = UUID.randomUUID().toString();\n\n    static {\n        logger.setLevel(Level.INFO);\n    }\n\n    public DistributedLockManager(final String host, final int port) {\n        this.host = host;\n        this.port = port;\n    }\n\n    private String encrypt(final String message) {\n        if (encryptionManager != null) {\n            return encryptionManager.encrypt(message);\n        }\n        return message;\n    }\n\n    /**\n     * Starts the connection with the lock server.\n     *\n     * @param password connection password\n     * @return {@code true} if successful\n     */\n    public boolean connectToServer(final char[] password) {\n        boolean result = false;\n\n        // If a password has been specified, create an EncryptionManager\n        if (password != null && password.length > 0) {\n            encryptionManager = new EncryptionManager(password);\n        }\n\n        final Bootstrap bootstrap = new Bootstrap();\n\n        eventLoopGroup = new NioEventLoopGroup();\n\n        bootstrap.group(eventLoopGroup)\n                .channel(NioSocketChannel.class)\n                .handler(new Initializer())\n                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ConnectionFactory.getConnectionTimeout() * 1000)\n                .option(ChannelOption.SO_KEEPALIVE, true);\n\n        try {\n            // Start the connection attempt.\n            channel = bootstrap.connect(host, port).sync().channel();\n\n            channel.writeAndFlush(encrypt(UUID_PREFIX + uuid) + EOL_DELIMITER).sync();   // send this channels uuid\n\n            result = true;\n            logger.info(\"Connection made with Distributed Lock Server\");\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, \"Failed to connect to Distributed Lock Server\", e);\n            disconnectFromServer();\n        }\n\n        return result;\n    }\n\n    /**\n     * Disconnects from the lock server.\n     */\n    public void disconnectFromServer() {\n\n        try {\n            channel.close().sync();\n        } catch (InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        executorService.shutdown();\n        eventLoopGroup.shutdownGracefully();\n\n        eventLoopGroup = null;\n        channel = null;\n\n        logger.info(\"Disconnected from the Distributed Lock Server\");\n    }\n\n    @Override\n    public synchronized ReentrantReadWriteLock getLock(final String lockId) {\n        return lockMap.computeIfAbsent(lockId, k -> new DistributedReadWriteLock(lockId));\n    }\n\n    private CountDownLatch getLatch(final String lockMessage) {\n        latchLock.lock();\n\n        try {\n            return latchMap.computeIfAbsent(lockMessage, k -> new CountDownLatch(1));\n        } finally {\n            latchLock.unlock();\n        }\n    }\n\n    private void lock(final String lockId, final String type) {\n        changeLockState(lockId, type, DistributedLockServer.LOCK);\n    }\n\n    private void unlock(final String lockId, final String type) {\n        changeLockState(lockId, type, DistributedLockServer.UNLOCK);\n    }\n\n    private void changeLockState(final String lockId, final String type, final String lockState) {\n        final String threadId = uuid + '-' + Thread.currentThread().getId();\n        final String lockMessage = MessageFormat.format(PATTERN, lockState, lockId, threadId, type);\n\n        final CountDownLatch responseLatch = getLatch(lockMessage);\n\n        //noinspection SynchronizationOnLocalVariableOrMethodParameter\n        synchronized (responseLatch) {   // synchronize on the lock to prevent concurrency errors\n\n            boolean result = false;\n\n            try {\n\n                // send the message to the server and wait until it if flushed\n                channel.writeAndFlush(encrypt(lockMessage) + EOL_DELIMITER).sync();\n\n                for (int i = 0; i < 2; i++) {\n                    result = responseLatch.await(45L, TimeUnit.SECONDS);\n\n                    if (!result) {\n                        logger.log(Level.WARNING, \"Excessive wait for release of the lock latch for: {0}\", lockId);\n                    } else {\n                        break;\n                    }\n                }\n\n                if (!result) {  // check for a failed release or deadlock\n                    logger.log(Level.SEVERE, \"Failed to release the lock latch for: {0}\", lockId);\n\n                    latchLock.lock();\n\n                    try {\n                        responseLatch.countDown();  // force a countdown to occur\n                        latchMap.remove(lockId);    // force removal\n                    } finally {\n                        latchLock.unlock();\n                    }\n                }\n            } catch (InterruptedException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }\n    }\n\n    private void processMessage(final String lockMessage) {\n\n        final String plainMessage;\n\n        if (encryptionManager != null) {\n            plainMessage = encryptionManager.decrypt(lockMessage);\n        } else {\n            plainMessage = lockMessage;\n        }\n\n        //logger.info(plainMessage);\n\n        /* lock_action, lock_id, thread_id, lock_type */\n        // unlock,account,3456384756384563,read\n        // lock,account,3456384756384563,write\n\n        latchLock.lock();\n\n        try {\n            final CountDownLatch responseLatch = getLatch(plainMessage);\n\n            responseLatch.countDown();  // this should release the responseLatch allowing a blocked thread to continue\n            latchMap.remove(plainMessage);    // remove the used up latch\n        } finally {\n            latchLock.unlock();\n        }\n    }\n\n    private static class LockManagerThreadFactory implements ThreadFactory {\n        private final AtomicLong counter = new AtomicLong();\n\n        @Override\n        public Thread newThread(final Runnable r) {\n            return new Thread(r, \"jGnash Distributed Lock Manager \" + counter.incrementAndGet());\n        }\n    }\n\n    private class Initializer extends ChannelInitializer<SocketChannel> {\n\n        @Override\n        public void initChannel(final SocketChannel ch) {\n            ChannelPipeline pipeline = ch.pipeline();\n\n            // Add the text line codec combination first,\n            pipeline.addLast(\"framer\", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));\n            pipeline.addLast(\"decoder\", new StringDecoder(CharsetUtil.UTF_8));\n            pipeline.addLast(\"encoder\", new StringEncoder(CharsetUtil.UTF_8));\n\n            // and then business logic.\n            pipeline.addLast(\"handler\", new ClientHandler());\n        }\n    }\n\n    /**\n     * Handles a client-side channel.\n     */\n    @ChannelHandler.Sharable\n    private class ClientHandler extends ChannelInboundHandlerAdapter {\n\n        @Override\n        public void channelRead(final ChannelHandlerContext ctx, final Object msg) {\n            executorService.submit(() -> {\n                processMessage(msg.toString());\n\n                ReferenceCountUtil.release(msg);\n            });\n        }\n\n        @Override\n        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {\n            super.exceptionCaught(ctx, cause);\n            logger.log(Level.WARNING, \"Unexpected exception from downstream.\", cause);\n            ctx.close();\n        }\n    }\n\n    private class DistributedReadWriteLock extends ReentrantReadWriteLock {\n\n        private final String lockId;\n\n        private final DistributedReadWriteLock.ReadLock readLock;\n\n        private final DistributedReadWriteLock.WriteLock writeLock;\n\n        DistributedReadWriteLock(final String lockId) {\n            super();\n\n            this.lockId = lockId;\n\n            readLock = new DistributedReadWriteLock.ReadLock(this);\n            writeLock = new DistributedReadWriteLock.WriteLock(this);\n        }\n\n        @Override\n        @NotNull\n        public ReentrantReadWriteLock.ReadLock readLock() {\n            return readLock;\n        }\n\n        @Override\n        @NotNull\n        public ReentrantReadWriteLock.WriteLock writeLock() {\n            return writeLock;\n        }\n\n        class ReadLock extends ReentrantReadWriteLock.ReadLock {\n\n            ReadLock(final ReentrantReadWriteLock lock) {\n                super(lock);\n            }\n\n            @Override\n            public void lock() {\n                DistributedLockManager.this.lock(lockId, DistributedLockServer.LOCK_TYPE_READ);\n                super.lock();\n            }\n\n            @Override\n            public void unlock() {\n                DistributedLockManager.this.unlock(lockId, DistributedLockServer.LOCK_TYPE_READ);\n                super.unlock();\n            }\n        }\n\n        class WriteLock extends ReentrantReadWriteLock.WriteLock {\n\n            WriteLock(final ReentrantReadWriteLock lock) {\n                super(lock);\n            }\n\n            @Override\n            public void lock() {\n                DistributedLockManager.this.lock(lockId, DistributedLockServer.LOCK_TYPE_WRITE);\n                super.lock();\n            }\n\n            @Override\n            public void unlock() {\n                DistributedLockManager.this.unlock(lockId, DistributedLockServer.LOCK_TYPE_WRITE);\n                super.unlock();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/concurrent/DistributedLockServer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.concurrent;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.net.ConnectionFactory;\nimport jgnash.util.EncodeDecode;\nimport jgnash.util.EncryptionManager;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandlerAdapter;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.group.ChannelGroup;\nimport io.netty.channel.group.DefaultChannelGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.util.CharsetUtil;\nimport io.netty.util.ReferenceCountUtil;\nimport io.netty.util.concurrent.GlobalEventExecutor;\n\nimport static jgnash.net.ConnectionFactory.MILLIS_PER_SECOND;\n\n/**\n * Distributed Lock Server.\n *\n * @author Craig Cavanaugh\n */\npublic class DistributedLockServer {\n\n    private static final Logger logger = Logger.getLogger(DistributedLockServer.class.getName());\n\n    // there needs to be to 2 threads to ensure order of operations and allow for unlocking without blocking\n    private final ExecutorService executorService = Executors.newFixedThreadPool(2, new LockServerThreadFactory());\n\n    private final ChannelGroup channelGroup = new DefaultChannelGroup(\"lock-server\", GlobalEventExecutor.INSTANCE);\n\n    private NioEventLoopGroup eventLoopGroup;\n\n    private final int port;\n\n    private final Map<String, ReadWriteLock> lockMap = new HashMap<>();\n\n    private final Map<ChannelHandlerContext, String> handlerContextMap = new HashMap<>();\n\n    static final String LOCK = \"lock\";\n\n    static final String UNLOCK = \"unlock\";\n\n    static final String LOCK_TYPE_READ = \"READ\";\n\n    static final String LOCK_TYPE_WRITE = \"WRITE\";\n\n    private static final String EOL_DELIMITER = \"\\r\\n\";\n\n    private EncryptionManager encryptionManager = null;\n\n    public DistributedLockServer(final int port) {\n        this.port = port;\n    }\n\n    private String encrypt(final String message) {\n        if (encryptionManager != null) {\n            return encryptionManager.encrypt(message);\n        }\n        return message;\n    }\n\n    private void processMessage(final ChannelHandlerContext ctx, final String msg) {\n\n        final String message;\n\n        if (encryptionManager != null) {\n            message = encryptionManager.decrypt(msg);\n        } else {\n            message = msg;\n        }\n\n        // Look for a uuid announcement for a channel\n        if (message.startsWith(DistributedLockManager.UUID_PREFIX)) {\n            handlerContextMap.put(ctx, message.substring(DistributedLockManager.UUID_PREFIX.length()));\n            return;\n        }\n\n        /* lock_action, lock_id, thread_id, lock_type */\n        // unlock,account,1194917570,read\n        // lock,account,1194917570,write\n\n        // decode the message into it's parts\n        final String[] strings = EncodeDecode.decodeStringCollection(message).toArray(new String[4]);\n\n        final String action = strings[0];\n        final String lockId = strings[1];\n        final String remoteThread = strings[2];\n        final String lockType = strings[3];\n\n        final ReadWriteLock lock = getLock(lockId);\n\n        try {\n\n            // request a lock or unlock.  This may block\n            switch (action) {\n                case LOCK:\n                    switch (lockType) {\n                        case LOCK_TYPE_READ:\n                            lock.lockForRead(remoteThread);\n                            break;\n                        case LOCK_TYPE_WRITE:\n                            lock.lockForWrite(remoteThread);\n                            break;\n                        default:\n                            break;\n                    }\n                    break;\n                case UNLOCK:\n                    switch (lockType) {\n                        case LOCK_TYPE_READ:\n                            lock.unlockRead(remoteThread);\n                            break;\n                        case LOCK_TYPE_WRITE:\n                            lock.unlockWrite(remoteThread);\n                            break;\n                        default:\n                            break;\n                    }\n                    break;\n            }\n\n            // return the message as an acknowledgment lock state has changed\n            if (ctx.channel().isOpen()) {\n                ctx.writeAndFlush(encrypt(message) + EOL_DELIMITER).sync();\n            }\n        } catch (final Exception e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private ReadWriteLock getLock(final String lockId) {\n        return lockMap.computeIfAbsent(lockId, k -> new ReadWriteLock(lockId));\n    }\n\n    public boolean startServer(final char[] password) {\n        boolean result = false;\n\n        // If a password has been specified, create an EncryptionManager\n        if (password != null && password.length > 0) {\n            encryptionManager = new EncryptionManager(password);\n        }\n\n        eventLoopGroup = new NioEventLoopGroup();\n\n        final ServerBootstrap bootstrap = new ServerBootstrap();\n\n        try {\n            bootstrap.group(eventLoopGroup)\n                    .channel(NioServerSocketChannel.class)\n                    .childHandler(new Initializer())\n                    .childOption(ChannelOption.SO_KEEPALIVE, true);\n\n            final ChannelFuture future = bootstrap.bind(port);\n            future.sync();\n\n            if (future.isDone() && future.isSuccess()) {\n                logger.info(\"Distributed Lock Server started successfully\");\n                result = true;\n            } else {\n                logger.info(\"Failed to start the Distributed Lock Server\");\n            }\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            stopServer();\n        }\n\n        return result;\n    }\n\n    public void stopServer() {\n        try {\n            channelGroup.close().sync();\n            executorService.shutdown();\n            eventLoopGroup.shutdownGracefully();\n\n            eventLoopGroup = null;\n\n            logger.info(\"Distributed Lock Server Stopped\");\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private static class LockServerThreadFactory implements ThreadFactory {\n        private final AtomicLong counter = new AtomicLong();\n\n        @Override\n        public Thread newThread(final Runnable r) {\n            return new Thread(r, \"jGnash Distributed Lock Server \" + counter.incrementAndGet());\n        }\n    }\n\n\n    @ChannelHandler.Sharable\n    private class ServerHandler extends ChannelInboundHandlerAdapter {\n\n        @Override\n        public void channelActive(final ChannelHandlerContext ctx) {\n            channelGroup.add(ctx.channel()); // maintain channels\n\n            logger.log(Level.INFO, \"Remote connection from: {0}\", ctx.channel().remoteAddress().toString());\n        }\n\n        @Override\n        public void channelInactive(final ChannelHandlerContext ctx) throws Exception {\n            logger.log(Level.INFO, \"Remote connection {0} closed\", ctx.channel().remoteAddress().toString());\n\n            final String uuid = handlerContextMap.get(ctx);\n\n            // Search through the lock map and remove any stale locks\n            if (uuid != null) {\n                for (ReadWriteLock readWriteLock : lockMap.values()) {  // look at every lock\n\n                    // if the remoteThread starts with the uuid, request a cleanup\n                    // cleanup a stale lock\n                    readWriteLock.readingThreads.keySet().stream().filter(remoteThread ->\n                            remoteThread.startsWith(uuid)).forEach(readWriteLock::cleanupStaleThread);\n\n                    if (readWriteLock.hasWriteThread(uuid)) {\n                        readWriteLock.cleanupStaleWriteThread();\n                    }\n                }\n            }\n\n            handlerContextMap.remove(ctx);\n            channelGroup.remove(ctx.channel());\n            super.channelInactive(ctx);\n        }\n\n        @Override\n        public void channelRead(final ChannelHandlerContext ctx, final Object msg) {\n            executorService.submit(() -> {\n                processMessage(ctx, msg.toString());\n                ReferenceCountUtil.release(msg);\n            });\n        }\n\n        @Override\n        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {\n            logger.log(Level.WARNING, \"Unexpected exception from downstream.\", cause);\n            ctx.close();\n        }\n    }\n\n    private class Initializer extends ChannelInitializer<SocketChannel> {\n\n        @Override\n        public void initChannel(final SocketChannel ch) {\n            ChannelPipeline pipeline = ch.pipeline();\n\n            // Add the text line codec combination first,\n            pipeline.addLast(\"framer\", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));\n\n            // the encoder and decoder are static as these are sharable\n            pipeline.addLast(\"decoder\", new StringDecoder(CharsetUtil.UTF_8));\n            pipeline.addLast(\"encoder\", new StringEncoder(CharsetUtil.UTF_8));\n\n            // and then business logic.\n            pipeline.addLast(\"handler\", new ServerHandler());\n        }\n    }\n\n    /**\n     * Reentrant Read Write lock.\n     * <p>\n     * A unique integer must be supplied to identify the thread instead of the current thread.\n     */\n    private static class ReadWriteLock {\n\n        private final String id;\n\n        /**\n         * The key is the uuid of the manager plus the remote thread id.\n         * <p>\n         * uuid-integer\n         */\n        private final Map<String, Integer> readingThreads = new ConcurrentHashMap<>();\n\n        private int writeAccesses = 0;\n        private int writeRequests = 0;\n        private String writingThread = null;\n\n        private ReadWriteLock(final String id) {\n            this.id = id;\n        }\n\n        synchronized boolean hasWriteThread(final String id) {\n            boolean result = false;\n\n            if (writingThread != null) {\n                result = writingThread.startsWith(id);\n            }\n\n            return result;\n        }\n\n        synchronized void cleanupStaleThread(final String remoteThread) {\n            if (readingThreads.containsKey(remoteThread)) {\n                unlockRead(remoteThread);\n                logger.log(Level.WARNING, \"Removed a stale read lock for: {0}\", id);\n            }\n\n            if (writingThread != null && writingThread.equals(remoteThread)) {\n                unlockWrite(remoteThread);\n                logger.log(Level.WARNING, \"Removed a stale write lock for: {0}\", id);\n            }\n        }\n\n        synchronized void cleanupStaleWriteThread() {\n            if (readingThreads.containsKey(writingThread)) {\n                unlockRead( writingThread);\n                logger.log(Level.WARNING, \"Removed a stale read lock for: {0}\", id);\n            }\n\n            if (writingThread != null) {\n                unlockWrite( writingThread);\n                logger.log(Level.WARNING, \"Removed a stale write lock for: {0}\", id);\n            }\n        }\n\n        synchronized void lockForRead(final String remoteThread) throws InterruptedException {\n\n            while (!canGrantReadAccess(remoteThread)) {\n                // wait for a maximum of 2X the network timout\n                wait((long) ConnectionFactory.getConnectionTimeout() * MILLIS_PER_SECOND * 2);\n            }\n\n            readingThreads.put(remoteThread, (getReadHoldCount(remoteThread) + 1));\n        }\n\n        synchronized void lockForWrite(final String remoteThread) throws InterruptedException {\n            writeRequests++;\n\n            while (!canGrantWriteAccess(remoteThread)) {\n                // wait for a maximum of 2X the network timout\n                wait((long) ConnectionFactory.getConnectionTimeout() * MILLIS_PER_SECOND * 2);\n            }\n\n            writeRequests--;\n            writeAccesses++;   // bump, if greater than 1, then the lock is reentrant\n            writingThread = remoteThread;\n        }\n\n        synchronized void unlockRead(final String remoteThread) {\n\n            if (!isReadLockedByCurrentThread(remoteThread)) {\n                throw new IllegalMonitorStateException(\"Remote Thread: \" + remoteThread + \" does not hold a read lock for: \" + id);\n            }\n\n            int holdCount = getReadHoldCount(remoteThread);\n\n            if (holdCount == 1) {\n                readingThreads.remove(remoteThread);\n            } else {\n                readingThreads.put(remoteThread, (holdCount - 1));\n            }\n\n            notifyAll();\n        }\n\n        synchronized void unlockWrite(final String remoteThread) {\n\n            if (!isWriteLockedByCurrentThread(remoteThread)) {\n                throw new IllegalMonitorStateException(\"Remote Thread: \" + remoteThread + \" does not hold the write lock for: \" + id);\n            }\n\n            writeAccesses--;\n\n            if (writeAccesses == 0) {\n                writingThread = null;\n            }\n\n            notifyAll();\n        }\n\n        private synchronized boolean canGrantReadAccess(final String remoteThread) {\n\n            if (isWriteLockedByCurrentThread(remoteThread)) { // lock down grade is allowed\n                return true;\n            }\n\n            if (writingThread != null) {\n                return false;\n            }\n            if (isReadLockedByCurrentThread(remoteThread)) {\n                return true;\n            }\n\n            return writeRequests <= 0;\n        }\n\n        private synchronized boolean canGrantWriteAccess(final String remoteThread) {\n\n            if (!readingThreads.isEmpty()) {\n                return false;\n            }\n            if (writingThread == null) {\n                return true;\n            }\n            return isWriteLockedByCurrentThread(remoteThread); // reentrant write\n        }\n\n        private synchronized int getReadHoldCount(final String remoteThread) {\n            final Integer accessCount = readingThreads.get(remoteThread);\n\n            return Objects.requireNonNullElse(accessCount, 0);\n\n        }\n\n        private synchronized boolean isReadLockedByCurrentThread(final String remoteThread) {\n            return readingThreads.get(remoteThread) != null;\n        }\n\n        private synchronized boolean isWriteLockedByCurrentThread(final String remoteThread) {\n            if (writingThread != null) {\n                return writingThread.equals(remoteThread);\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/concurrent/LocalLockManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.concurrent;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * Lock manager for local engine instances.\n *\n * @author Craig Cavanaugh\n */\npublic class LocalLockManager implements LockManager {\n    private final Map<String, ReentrantReadWriteLock> lockMap = new ConcurrentHashMap<>();\n\n    @Override\n    public ReentrantReadWriteLock getLock(final String lockId) {\n        return lockMap.computeIfAbsent(lockId, k -> new ReentrantReadWriteLock());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/concurrent/LockManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.concurrent;\n\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * Lock manger for all engine operations.\n *\n * Locks may be local or distributed depending on connection type\n *\n * @author Craig Cavanaugh\n */\npublic interface LockManager {\n\n    /**\n     * Returns a named {@link ReentrantReadWriteLock}.\n     * Locks are cached and reused\n     *\n     * @param lockId id of the lock\n     *\n     * @return a new or cached ReentrantReadWriteLock\n     */\n    ReentrantReadWriteLock getLock(final String lockId);\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/concurrent/Priority.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.concurrent;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * Immutable priority object that ensures first-in-first-out if tasks have the same priority.\n *\n * Smaller values will have more priority.\n *\n * @author Craig Cavanaugh\n */\npublic class Priority implements Comparable<Priority> {\n\n    public static final int SYSTEM = 1;\n\n    public static final int BACKGROUND = 100;\n\n    private static final AtomicLong atomicLongSequence = new AtomicLong(0);\n\n    private final long sequence;\n\n    private final int priority;\n\n    Priority(final int priority) {\n        sequence = atomicLongSequence.getAndIncrement();\n        this.priority = priority;\n    }\n\n    int getPriority() {\n        return priority;\n    }\n\n    @Override\n    public int compareTo(final Priority other) {\n        int result = Integer.compare(priority, other.priority);\n\n        if (result == 0) {  // ensure fifo if the priority is the same\n            result = (sequence < other.sequence ? -1 : 1);\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/concurrent/PriorityThreadPoolExecutor.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.concurrent;\n\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.FutureTask;\nimport java.util.concurrent.PriorityBlockingQueue;\nimport java.util.concurrent.RunnableFuture;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Decorator around a {@code ThreadPoolExecutor} that provides execution priority.\n *\n * Callables with the same priority level are FIFO'd.\n *\n * @author Craig Cavanaugh\n */\npublic class PriorityThreadPoolExecutor {\n\n    private final ThreadPoolExecutor threadPoolExecutor;\n\n    private final PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<>();\n\n    public PriorityThreadPoolExecutor(ThreadFactory threadFactory) {\n        threadPoolExecutor = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS,\n                queue, threadFactory) {\n\n            // Wraps a Callable with a FutureTaskWrapper that respects the Priority\n            @Override\n            protected <v> RunnableFuture<v> newTaskFor(final Callable<v> c) {\n                return new FutureTaskWrapper<>((PriorityCallable<v>) c);\n            }\n        };\n\n        threadPoolExecutor.allowCoreThreadTimeOut(false);\n    }\n\n    public PriorityThreadPoolExecutor() {\n        this(Executors.defaultThreadFactory());\n    }\n\n    private  <T> Future<T> submit(final Callable<T> callable, final Priority priority) {\n        return threadPoolExecutor.submit(new PriorityCallable<>() {\n            @Override\n            public Priority getPriority() {\n                return priority;\n            }\n\n            @Override\n            public T call() throws Exception {\n                return callable.call();\n            }\n        });\n    }\n\n    public <T> Future<T> submit(final Callable<T> callable, final int priority) {\n        return submit(callable, new Priority(priority));\n    }\n\n    public <T> Future<T> submit(final Callable<T> callable) {\n        return submit(callable, Priority.SYSTEM);\n    }\n\n    public void shutdown() {\n\n        /* Remove any non-critical system tasks from the executor first */\n        for (final Runnable runnable : queue.toArray(new Runnable[0])) {\n            if (((FutureTaskWrapper<?>)runnable).getPriorityCallable().getPriority().getPriority() != Priority.SYSTEM) {\n                threadPoolExecutor.remove(runnable);\n            }\n        }\n\n        threadPoolExecutor.shutdown();\n    }\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public List<Runnable> shutdownNow() {\n        return threadPoolExecutor.shutdownNow();\n    }\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {\n        return threadPoolExecutor.awaitTermination(timeout, unit);\n    }\n\n    //A future task that wraps around the priority task to be used in the queue\n    static class FutureTaskWrapper<T> extends FutureTask<T> implements Comparable<FutureTaskWrapper<T>> {\n        private final PriorityCallable<T> priorityCallable;\n\n        FutureTaskWrapper(final PriorityCallable<T> priorityCallable) {\n            super(priorityCallable);\n            this.priorityCallable = priorityCallable;\n        }\n\n        PriorityCallable<T> getPriorityCallable() {\n            return priorityCallable;\n        }\n\n        @Override\n        public int compareTo(final FutureTaskWrapper<T> other) {\n            return priorityCallable.getPriority().compareTo(other.priorityCallable.getPriority());\n        }\n    }\n\n    private interface PriorityCallable<V> extends Callable<V> {\n        Priority getPriority ();\n    }\n}\n\n\n\n\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/AbstractDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport jgnash.engine.StoredObject;\n\n/**\n * Basic DAO.\n *\n * @author Craig Cavanaugh\n */\npublic abstract class AbstractDAO implements DAO {\n\n    protected final AtomicBoolean dirtyFlag = new AtomicBoolean(false);\n\n    protected static <T extends StoredObject> List<T> stripMarkedForRemoval(final List<T> list) {\n        list.removeIf(StoredObject::isMarkedForRemoval);\n        return list;\n    }\n\n    @Override\n    public boolean isDirty() {\n        return dirtyFlag.get();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/AccountDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.SecurityNode;\n\n/**\n * Account DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface AccountDAO extends DAO {\n\n    RootAccount getRootAccount();\n\n    List<Account> getAccountList();\n\n    boolean addAccount(Account parent, final Account child);\n\n    boolean addRootAccount(RootAccount account);\n\n    /**\n     * Adds a SecurityNode from a InvestmentAccount.\n     *\n     * @param account account to add security to\n     * @param node    security to add\n     * @return true if success\n     */\n    boolean addAccountSecurity(final Account account, final SecurityNode node);\n\n    /**\n     * Returns a list of IncomeAccounts.\n     *\n     * @return list of income accounts\n     */\n    List<Account> getIncomeAccountList();\n\n    /**\n     * Returns a list of ExpenseAccounts.\n     *\n     * @return list of expense accounts\n     */\n    List<Account> getExpenseAccountList();\n\n    /**\n     * Returns a list of InvestmentAccounts.\n     *\n     * @return list of investment accounts\n     */\n    List<Account> getInvestmentAccountList();\n\n    Account getAccountByUuid(final UUID uuid);\n\n    boolean updateAccount(Account account);\n\n    /**\n     * Toggles the visibility of an account given its ID.\n     *\n     * @param account The account to toggle visibility\n     * @return <tt>true</tt> if the supplied account ID was found\n     *         <tt>false</tt> if the supplied account ID was not found\n     */\n    boolean toggleAccountVisibility(final Account account);\n\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/BudgetDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport jgnash.engine.budget.Budget;\n\n/**\n * Budget DAO.\n *\n * @author Craig Cavanaugh\n */\npublic interface BudgetDAO extends DAO {\n\n    boolean add(Budget budget);\n\n    boolean update(Budget budget);\n\n    List<Budget> getBudgets();\n\n    Budget getBudgetByUuid(final UUID uuid);\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/CommodityDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.util.NotNull;\n\n/**\n * Commodity DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface CommodityDAO extends DAO {\n\n    boolean addCommodity(CommodityNode node);\n\n    /**\n     * Call after a {@code ExchangeRateHistoryNode} has been added.  This pushes the update\n     * to the underlying database\n     * @param rate ExchangeRate to update\n     *\n     * @return true if successful\n     */\n    boolean addExchangeRateHistory(final ExchangeRate rate);\n\n    /**\n     * Call after a {@code SecurityHistoryNode} has been added.  This pushes the update\n     * to the underlying database\n     * @param node {@code SecurityNode} to update\n     * @param historyNode {@code SecurityHistoryNode to add}\n     *\n     * @return true if successful\n     */\n    boolean addSecurityHistory(final SecurityNode node, final SecurityHistoryNode historyNode);\n\n    /**\n     * Call after a {@code SecurityHistoryEvent} has been added.  This pushes the update\n     * to the underlying database\n     * @param node {@code SecurityNode} to update\n     * @param historyEvent {@code SecurityHistoryEvent to add}\n     *\n     * @return true if successful\n     */\n    boolean addSecurityHistoryEvent(final SecurityNode node, final SecurityHistoryEvent historyEvent);\n\n    /**\n     * Returns the active currencies.\n     *\n     * @return set of active currencies\n     */\n    Set<CurrencyNode> getActiveCurrencies();\n\n    List<CurrencyNode> getCurrencies();\n\n    CurrencyNode getCurrencyByUuid(final UUID uuid);\n\n    SecurityNode getSecurityByUuid(final UUID uuid);\n\n    ExchangeRate getExchangeNode(final String rateId);\n\n    ExchangeRate getExchangeRateByUuid(final UUID uuid);\n\n    @NotNull List<SecurityNode> getSecurities();\n\n    /**\n     * Call after a {@code ExchangeRateHistoryNode} has been removed.  This pushes the update\n     * to the underlying database\n     * @param rate ExchangeRate to update\n     *\n     * @return true if successful\n     */\n    boolean removeExchangeRateHistory(final ExchangeRate rate);\n\n    /**\n     * Call after a {@code SecurityHistoryNode} has been removed.  This pushes the update\n     * to the underlying database\n     * @param node {@code SecurityNode} to update\n     * @param historyNode {@code SecurityHistoryNode} to remove\n     *\n     * @return true if successful\n     */\n    boolean removeSecurityHistory(final SecurityNode node, final SecurityHistoryNode historyNode);\n\n    /**\n     * Call after a {@code SecurityHistoryEvent} has been removed.  This pushes the update\n     * to the underlying database\n     * @param node {@code SecurityNode} to update\n     * @param historyEvent {@code SecurityHistoryEvent} to remove\n     *\n     * @return true if successful\n     */\n    boolean removeSecurityHistoryEvent(final SecurityNode node, final SecurityHistoryEvent historyEvent);\n\n    void addExchangeRate(ExchangeRate eRate);\n\n    boolean updateCommodityNode(final CommodityNode node);\n\n    List<ExchangeRate> getExchangeRates();\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/ConfigDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport jgnash.engine.Config;\n\n/**\n * Configuration DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface ConfigDAO extends DAO {\n\n    Config getDefaultConfig();\n\n    void update(Config config);\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/DAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.UUID;\n\n/**\n * Base DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface DAO {\n    <T> T getObjectByUuid(final Class<T> tClass, final UUID uuid);\n\n    boolean isDirty();\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/EngineDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\n\nimport jgnash.engine.StoredObject;\n\n/**\n * Engine DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface EngineDAO extends DAO {\n\n    AccountDAO getAccountDAO();\n\n    BudgetDAO getBudgetDAO();\n\n    CommodityDAO getCommodityDAO();\n\n    ConfigDAO getConfigDAO();\n\n    RecurringDAO getRecurringDAO();\n\n    TagDAO getTagDAO();\n\n    TransactionDAO getTransactionDAO();\n\n    TrashDAO getTrashDAO();\n\n    List<StoredObject> getStoredObjects();\n\n    <T extends StoredObject> List<T> getStoredObjects(Class<T> tClass);\n\n    /**\n     * Force the object to be reloaded from the underlying database.\n     * <p>\n     * Intended for client / server use.\n     *\n     * @param object object to refresh\n     */\n    void refresh(StoredObject object);\n\n    /**\n     * Allows for a bulk update of StoredObjects\n     * <p>\n     * This is intended for in place data updates and use should be minimal\n     *\n     * @param objectList list of {@code StoredObject} to update\n     */\n    void bulkUpdate(List<? extends StoredObject> objectList);\n\n    void shutdown();\n\n    default boolean isRemote() {\n        return false;\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/RecurringDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport jgnash.engine.recurring.Reminder;\n\n/**\n * Reminder DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface RecurringDAO extends DAO {\n\n    List<Reminder> getReminderList();\n\n    boolean addReminder(Reminder reminder);\n\n    Reminder getReminderByUuid(final UUID uuid);\n\n    boolean updateReminder(Reminder reminder);\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/TagDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.Set;\n\nimport jgnash.engine.Tag;\n\n/**\n * Tag DAO.\n *\n * @author Craig Cavanaugh\n */\npublic interface TagDAO extends DAO {\n\n    boolean add(Tag tag);\n\n    boolean update(Tag tag);\n\n    Set<Tag> getTags();\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/TransactionDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport jgnash.engine.Transaction;\n\n/**\n * Transaction DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface TransactionDAO extends DAO {\n\n    /**\n     * Returns a list of transactions.\n     *\n     * @return List of transactions\n     */\n    List<Transaction> getTransactions();\n\n    boolean addTransaction(Transaction transaction);\n\n    Transaction getTransactionByUuid(final UUID uuid);\n\n    boolean removeTransaction(Transaction transaction);\n\n    /**\n     * Returns a list of transactions with external links.\n     *\n     * @return List of transactions\n     */\n    List<Transaction> getTransactionsWithAttachments();\n\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/dao/TrashDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.dao;\n\nimport java.util.List;\n\nimport jgnash.engine.TrashObject;\n\n/**\n * Trash DAO Interface.\n *\n * @author Craig Cavanaugh\n */\npublic interface TrashDAO extends DAO {\n\n    List<TrashObject> getTrashObjects();\n\n    void add(TrashObject trashObject);\n\n    void remove(TrashObject trashObject);\n\n    void addEntityTrash(Object entity);\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/AbstractJpaDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.ConcurrentModificationException;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.NoResultException;\nimport javax.persistence.PersistenceException;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\n\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.concurrent.PriorityThreadPoolExecutor;\nimport jgnash.engine.dao.AbstractDAO;\nimport jgnash.engine.dao.DAO;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.NotNull;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Abstract JPA DAO.  Provides basic framework to work with the {@link EntityManager} in a thread safe manner.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractJpaDAO extends AbstractDAO implements DAO {\n\n    /**\n     * The {@link EntityManager} is not thread safe.  All interaction should be wrapped with this lock\n     */\n    static final ReentrantLock emLock = new ReentrantLock();\n\n    /**\n     * This ExecutorService is to be used whenever the entity manager is\n     * accessed because the EntityManager is not thread safe, but we want to return from some methods without blocking\n     */\n    static PriorityThreadPoolExecutor executorService =\n            new PriorityThreadPoolExecutor(new DefaultDaemonThreadFactory(\"JPA Priority Executor\"));\n\n    /**\n     * Entity manager reference.\n     */\n    final EntityManager em;\n\n    /**\n     * Remote connection if {@code true}.\n     */\n    final boolean isRemote;\n\n    AbstractJpaDAO(final EntityManager entityManager, final boolean isRemote) {\n        Objects.requireNonNull(entityManager);\n\n        this.isRemote = isRemote;\n        em = entityManager;\n    }\n\n    static void shutDownExecutor() {\n        // Stop the shared executor server, wait for all tasks to complete\n\n        emLock.lock();\n\n        try {\n            executorService.shutdown();\n\n            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);\n\n            // Regenerate the executor service\n            executorService = new PriorityThreadPoolExecutor();\n\n        } catch (final InterruptedException e) {\n            logSevere(AbstractJpaDAO.class, e);\n            Thread.currentThread().interrupt();\n        } finally {\n            emLock.unlock();\n        }\n    }\n\n    /**\n     * Returns a list of objects that are assignable from from the specified Class.\n     * <p>\n     * Objects marked for removal are not included\n     *\n     * @param clazz the Class to query for\n     * @param <T>   the type of class to query\n     * @return A list of type T containing objects of type clazz\n     */\n    @NotNull\n    public <T extends StoredObject> List<T> query(final Class<T> clazz) {\n\n        try {\n            final Future<List<T>> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final CriteriaBuilder cb = em.getCriteriaBuilder();\n                    final CriteriaQuery<T> cq = cb.createQuery(clazz);\n                    cq.from(clazz);\n\n                    final TypedQuery<T> query = em.createQuery(cq);\n\n                    // filtering though the stream is not has fast as performing the filter within the query, but it\n                    // ensures a ConcurrentModificationException is not thrown in rare circumstances by iterating.\n                    return query.getResultStream().filter(t -> !t.isMarkedForRemoval()).collect(Collectors.toList());\n\n                } catch (final ConcurrentModificationException | PersistenceException | IllegalStateException e1) {\n                    logSevere(AbstractJpaDAO.class, e1);\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            return future.get();    // block and return\n        } catch (final InterruptedException | ExecutionException e) {\n            logSevere(AbstractJpaDAO.class, e);\n            Thread.currentThread().interrupt();\n            return null;\n        }\n    }\n\n    /**\n     * Merge / Update the object in place.\n     *\n     * @param object {@link StoredObject} to merge\n     * @param <T>    the type of the value being merged\n     * @return the merged object or null if an error occurred\n     */\n    <T extends StoredObject> T merge(final T object) {\n        try {\n            final Future<T> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n                    T mergedObject = em.merge(object);\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return mergedObject;\n                } catch (final PersistenceException | IllegalStateException e1) {\n                    logSevere(AbstractJpaDAO.class, e1);\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            return future.get();    // block and return\n        } catch (final InterruptedException | ExecutionException e) {\n            logSevere(AbstractJpaDAO.class, e);\n            Thread.currentThread().interrupt();\n            return null;\n        }\n    }\n\n    /**\n     * Persists an object.\n     *\n     * @param objects {@link Object} to persist\n     * @return {@code true} if successful, {@code false} otherwise\n     */\n    boolean persist(final Object... objects) {\n        boolean result = false;\n\n        try {\n            final Future<Boolean> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    for (final Object object : objects) {\n                        em.persist(object);\n                    }\n\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return true;\n                } catch (final PersistenceException | IllegalStateException e1) {\n                    logSevere(AbstractJpaDAO.class, e1);\n                    return false;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            try {\n                result = future.get();  // block and return\n            } catch (final InterruptedException ie) {\n                Logger.getLogger(AbstractJpaDAO.class.getName()).log(Level.INFO, \"Interrupted while waiting for result\");\n                result = false;\n            }\n\n        } catch (final ExecutionException e2) {\n            logSevere(AbstractJpaDAO.class, e2);\n            Thread.currentThread().interrupt();\n        }\n\n        return result;\n    }\n\n    @Override\n    public <T> T getObjectByUuid(final Class<T> tClass, final UUID uuid) {\n        T object = null;\n\n        try {\n            final Future<T> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    return em.find(tClass, uuid);\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            object = future.get();  // block and return\n        } catch (final NoResultException e) {\n            Logger.getLogger(AbstractJpaDAO.class.getName()).log(Level.INFO, \"Did not find {0} for uuid: {1}\",\n                    new Object[]{tClass.getName(), uuid});\n        } catch (ExecutionException | InterruptedException e) {\n            logSevere(AbstractJpaDAO.class, e);\n            Thread.currentThread().interrupt();\n        }\n\n        return object;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/AbstractJpaDataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.function.DoubleConsumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.EntityManagerFactory;\nimport javax.persistence.Persistence;\n\nimport jgnash.engine.DataStore;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.attachment.DistributedAttachmentManager;\nimport jgnash.engine.attachment.LocalAttachmentManager;\nimport jgnash.engine.concurrent.DistributedLockManager;\nimport jgnash.engine.concurrent.LocalLockManager;\nimport jgnash.util.FileUtils;\n\nimport org.apache.commons.collections4.ListUtils;\nimport org.apache.commons.math3.util.Precision;\n\n/**\n * Abstract JPA DataStore.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractJpaDataStore implements DataStore {\n\n    private static final String SHUTDOWN = \"SHUTDOWN\";\n\n    private static final int PARTITION_SIZE = 200;\n\n    private EntityManager em;\n\n    private EntityManagerFactory factory;\n\n    private DistributedLockManager distributedLockManager;\n\n    private DistributedAttachmentManager distributedAttachmentManager;\n\n    private boolean local = true;\n\n    private String fileName;\n\n    private static final boolean DEBUG = false;\n\n    private char[] password;\n\n    static final Logger logger = Logger.getLogger(AbstractJpaDataStore.class.getName());\n\n    private void waitForLockFileRelease(final String fileName, final char[] password) {\n\n        // Explicitly force the database closed, Required for hsqldb and h2\n        SqlUtils.waitForLockFileRelease(getType(), fileName, getLockFileExtension(), password);\n    }\n\n    @Override\n    public void closeEngine() {\n        logger.info(\"Closing\");\n\n        if (em != null && factory != null) {\n            em.close();\n            factory.close();\n        } else {\n            logger.severe(\"The EntityManger was already null!\");\n        }\n\n        if (local) {\n            waitForLockFileRelease(fileName, password);\n        } else {\n            distributedLockManager.disconnectFromServer();\n            distributedAttachmentManager.disconnectFromServer();\n        }\n    }\n\n    @Override\n    public Engine getClientEngine(final String host, final int port, final char[] password, final String dataBasePath) {\n        final Properties properties\n                = JpaConfiguration.getClientProperties(getType(), dataBasePath, host, port, password);\n\n        Engine engine = null;\n\n        try {\n            if (SqlUtils.isConnectionValid(properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL))) {\n                factory = Persistence.createEntityManagerFactory(JpaConfiguration.UNIT_NAME, properties);\n\n                em = factory.createEntityManager();\n\n                if (em != null) {\n                    distributedLockManager = new DistributedLockManager(host, port\n                            + JpaNetworkServer.LOCK_SERVER_INCREMENT);\n\n                    boolean lockManagerResult = distributedLockManager.connectToServer(password);\n\n                    distributedAttachmentManager = new DistributedAttachmentManager(host, port\n                            + JpaNetworkServer.TRANSFER_SERVER_INCREMENT);\n\n                    boolean attachmentManagerResult = distributedAttachmentManager.connectToServer(password);\n\n                    if (attachmentManagerResult && lockManagerResult) {\n                        engine = new Engine(new JpaEngineDAO(em, true), distributedLockManager,\n                                distributedAttachmentManager, EngineFactory.DEFAULT);\n\n                        logger.info(\"Created local JPA container and engine\");\n                        fileName = null;\n                        local = false;\n                    } else {\n                        distributedLockManager.disconnectFromServer();\n                        distributedAttachmentManager.disconnectFromServer();\n\n                        em.close();\n                        factory.close();\n                        em = null;\n                        factory = null;\n                    }\n                }\n            }\n        } catch (final Exception e) {\n            logger.log(Level.SEVERE, e.toString(), e);\n        }\n\n        return engine;\n    }\n\n    @Override\n    public Engine getLocalEngine(final String fileName, final String engineName, final char[] password) {\n        Properties properties = JpaConfiguration.getLocalProperties(getType(), fileName, password, false);\n\n        Engine engine = null;\n\n        if (DEBUG) {\n            System.out.println(FileUtils.stripFileExtension(fileName));\n        }\n\n        if (!exists(fileName) && !initEmptyDatabase(fileName)) {\n            return null;\n        }\n\n        try {\n            if (!FileUtils.isFileLocked(fileName)) {\n                try {\n                    float fileVersion = SqlUtils.getFileVersion(fileName, password);\n\n                    if (Precision.equals(fileVersion, 3.5f)) {\n                        SqlUtils.dropColumn(fileName, password, \"TAG\", \"SHAPE\", \"ICONSET\");\n                        logger.info(\"Dropped old TAG columns\");\n                    }\n\n                    /* specifies the unit name and properties.  Unit name can be used to specify a different persistence\n                       unit defined in persistence.xml */\n                    factory = Persistence.createEntityManagerFactory(JpaConfiguration.UNIT_NAME, properties);\n                    em = factory.createEntityManager();\n\n                    logger.info(\"Created local JPA container and engine\");\n                    engine = new Engine(new JpaEngineDAO(em, false), new LocalLockManager(),\n                            new LocalAttachmentManager(), engineName);\n\n                    this.fileName = fileName;\n                    this.password = password.clone();   // clone to protect against side effects\n\n                    local = true;\n                } catch (final Exception e) {\n                    logger.log(Level.SEVERE, e.getMessage(), e);\n                }\n            }\n        } catch (final IOException e) {\n            logger.info(e.getLocalizedMessage());\n        }\n\n        return engine;\n    }\n\n\n    @Override\n    public String getFileName() {\n        return fileName;\n    }\n\n\n    @Override\n    public boolean isLocal() {\n        return local;\n    }\n\n    @Override\n    public void saveAs(final Path path, final Collection<StoredObject> objects, final DoubleConsumer percentComplete) {\n\n        final int collectionSize = objects.size();\n\n        // Remove the existing files so we don't mix entities and cause corruption\n        if (Files.exists(path)) {\n            deleteDatabase(path.toString());\n        }\n\n        if (initEmptyDatabase(path.toString())) {\n\n            final Properties properties = JpaConfiguration.getLocalProperties(getType(), path.toString(),\n                    new char[]{}, false);\n\n            EntityManagerFactory emFactory = null;\n            EntityManager entityManager = null;\n\n            try {\n                emFactory = Persistence.createEntityManagerFactory(JpaConfiguration.UNIT_NAME, properties);\n                entityManager = emFactory.createEntityManager();\n\n                final List<List<StoredObject>> partitions = ListUtils.partition(new ArrayList<>(objects), PARTITION_SIZE);\n\n                int writeCount = 0;\n\n                for (final List<StoredObject> partition : partitions) {\n                    entityManager.getTransaction().begin();\n\n                    for (final StoredObject o : partition) {\n                        entityManager.persist(o);\n                        writeCount++;\n\n                        percentComplete.accept((double) writeCount / (double) collectionSize);\n                    }\n\n                    entityManager.getTransaction().commit();\n                }\n            } catch (final Exception e) {\n                logger.log(Level.SEVERE, e.getMessage(), e);\n            } finally {\n                if (entityManager != null) {\n                    entityManager.close();\n                }\n\n                if (emFactory != null) {\n                    emFactory.close();\n                }\n            }\n\n            waitForLockFileRelease(path.toString(), new char[]{});\n        }\n    }\n\n    /**\n     * Returns the string representation of this {@code DataStore}.\n     *\n     * @return string representation of this {@code DataStore}.\n     */\n    @Override\n    public String toString() {\n        return getType().toString();\n    }\n\n    private boolean exists(final String fileName) {\n        return Files.exists(Paths.get(FileUtils.stripFileExtension(fileName) + getFileExt()));\n    }\n\n    /**\n     * Opens and closes the database in order to create a new file.\n     *\n     * @param fileName database file\n     * @return {@code true} if successful\n     */\n    private boolean initEmptyDatabase(final String fileName) {\n        boolean result = false;\n\n        final Properties properties = JpaConfiguration.getLocalProperties(getType(), fileName,\n                EngineFactory.EMPTY_PASSWORD, false);\n\n        final String url = properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL);\n\n        try (final Connection connection = DriverManager.getConnection(url)) {\n\n            // absolutely required for a correct shutdown\n            try (final PreparedStatement statement = connection.prepareStatement(SHUTDOWN)) {\n                statement.execute();\n            }\n\n            result = true;\n        } catch (final SQLException e) {\n            logger.log(Level.SEVERE, e.getMessage(), e);\n        }\n\n        waitForLockFileRelease(fileName, EngineFactory.EMPTY_PASSWORD);\n\n        logger.log(Level.INFO, \"Initialized an empty database for {0}\", fileName);\n\n        return result;\n    }\n\n    /**\n     * Deletes a database and associated files and directories.\n     *\n     * @param fileName one of the primary database files\n     */\n    protected abstract void deleteDatabase(final String fileName);\n\n    /**\n     * Return the extension used by the lock file with the preceding period.\n     *\n     * @return lock file extension\n     */\n    protected abstract String getLockFileExtension();\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaAccountDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.dao.AccountDAO;\nimport jgnash.util.LogUtil;\n\n/**\n * Account Jpa DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaAccountDAO extends AbstractJpaDAO implements AccountDAO {\n\n    private static final Logger logger = Logger.getLogger(JpaAccountDAO.class.getName());\n\n    JpaAccountDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#getRootAccount()\n     */\n    @Override\n    public RootAccount getRootAccount() {\n        RootAccount root = null;\n\n        try {\n            final Future<RootAccount> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final TypedQuery<RootAccount> q = em.createQuery(\"select a from RootAccount a\",\n                            RootAccount.class);\n                    final List<RootAccount> list = q.getResultList();\n\n                    if (list.size() == 1) {\n                        return list.get(0);\n                    } else if (list.size() > 1) {\n                        LogUtil.logSevere(JpaAccountDAO.class, new Exception(\"More than one RootAccount was found: \"\n                                                                                     + list.size()));\n\n                        for (final RootAccount rootAccount : list) {\n                            if (rootAccount.getChildCount() > 0) {\n                                return rootAccount;\n                            }\n                        }\n                    }\n\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            root = future.get();    // block and return\n        } catch (final ExecutionException | InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return root;\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#getAccountList()\n     */\n    @Override\n    public List<Account> getAccountList() {\n        return query(Account.class);\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#addAccount(jgnash.engine.Account, jgnash.engine.Account)\n     */\n    @Override\n    public boolean addAccount(final Account parent, final Account child) {\n        boolean result = false;\n\n        try {\n            final Future<Boolean> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    em.persist(child);\n                    em.merge(parent);\n\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return true;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            result = future.get();\n        } catch (final ExecutionException | InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return result;\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#addRootAccount(jgnash.engine.RootAccount)\n     */\n    @Override\n    public boolean addRootAccount(final RootAccount account) {\n        return persist(account);\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#addAccountSecurity(jgnash.engine.Account, jgnash.engine.SecurityNode)\n     */\n    @Override\n    public boolean addAccountSecurity(final Account account, final SecurityNode node) {\n        return merge(account) != null;\n    }\n\n    private List<Account> getAccountList(final AccountType type) {\n        List<Account> accountList = Collections.emptyList();\n\n        try {\n            final Future<List<Account>> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final String queryString\n                            = \"SELECT a FROM Account a WHERE a.accountType = :type AND a.markedForRemoval = false\";\n                    final TypedQuery<Account> query = em.createQuery(queryString, Account.class);\n                    query.setParameter(\"type\", type);\n\n                    return new ArrayList<>(query.getResultList());\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            accountList = future.get();\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return accountList;\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#getIncomeAccountList()\n     */\n    @Override\n    public List<Account> getIncomeAccountList() {\n        return getAccountList(AccountType.INCOME);\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#getExpenseAccountList()\n     */\n    @Override\n    public List<Account> getExpenseAccountList() {\n        return getAccountList(AccountType.EXPENSE);\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#getInvestmentAccountList()\n     */\n    @Override\n    public List<Account> getInvestmentAccountList() {\n\n        // do not use a parallel stream, result list is not thread safe\n        return query(Account.class).parallelStream().filter(a -> a.memberOf(AccountGroup.INVEST))\n                       .collect(Collectors.toList());\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#getAccountByUuid(java.util.UUID)\n     */\n    @Override\n    public Account getAccountByUuid(final UUID uuid) {\n        return getObjectByUuid(Account.class, uuid);\n    }\n\n    /*\n     * @see jgnash.engine.AccountDAOInterface#updateAccount(jgnash.engine.Account)\n     */\n    @Override\n    public boolean updateAccount(final Account account) {\n        return merge(account) != null;\n    }\n\n    /*\n     * @see jgnash.engine.dao.AccountDAO#toggleAccountVisibility(jgnash.engine.Account)\n     */\n    @Override\n    public boolean toggleAccountVisibility(final Account account) {\n        return merge(account) != null;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaBudgetDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport javax.persistence.EntityManager;\n\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.dao.BudgetDAO;\n\n/**\n * Budget DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaBudgetDAO extends AbstractJpaDAO implements BudgetDAO {\n\n    JpaBudgetDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    @Override\n    public boolean add(final Budget budget) {\n        return persist(budget);\n    }\n\n    @Override\n    public boolean update(final Budget budget) {\n        return merge(budget) != null;\n    }\n\n    @Override\n    public List<Budget> getBudgets() {\n        return query(Budget.class);\n    }\n\n    @Override\n    public Budget getBudgetByUuid(final UUID uuid) {\n        return getObjectByUuid(Budget.class, uuid);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaCommodityDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.dao.CommodityDAO;\nimport jgnash.util.NotNull;\n\n/**\n * Commodity DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaCommodityDAO extends AbstractJpaDAO implements CommodityDAO {\n\n    private static final Logger logger = Logger.getLogger(JpaCommodityDAO.class.getName());\n\n    JpaCommodityDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    /*\n     * @see jgnash.engine.CommodityDAOInterface#addCurrency(jgnash.engine.CommodityNode)\n     */\n    @Override\n    public boolean addCommodity(final CommodityNode node) {\n        return persist(node);\n    }\n\n    @Override\n    public boolean addSecurityHistory(final SecurityNode node, final SecurityHistoryNode historyNode) {\n        return persist(historyNode, node);\n    }\n\n    @Override\n    public boolean addSecurityHistoryEvent(final SecurityNode node, final SecurityHistoryEvent historyEvent) {\n        return persist(historyEvent, node);\n    }\n\n\n    @Override\n    public boolean removeSecurityHistory(final SecurityNode node, final SecurityHistoryNode historyNode) {\n        return persist(node, historyNode);\n    }\n\n    @Override\n    public boolean removeSecurityHistoryEvent(final SecurityNode node, final SecurityHistoryEvent historyEvent) {\n        return persist(node, historyEvent);\n    }\n\n    @Override\n    public boolean addExchangeRateHistory(final ExchangeRate rate) {\n        return merge(rate) != null;\n    }\n\n    @Override\n    public boolean removeExchangeRateHistory(final ExchangeRate rate) {\n        return merge(rate) != null;\n    }\n\n    /*\n     * @see jgnash.engine.CommodityDAOInterface#getCurrencies()\n     */\n    @Override\n    public List<CurrencyNode> getCurrencies() {\n        return query(CurrencyNode.class);\n    }\n\n    @Override\n    public CurrencyNode getCurrencyByUuid(final UUID uuid) {\n        return getObjectByUuid(CurrencyNode.class, uuid);\n    }\n\n    /*\n     * @see jgnash.engine.CommodityDAOInterface#getExchangeNode(java.lang.String)\n     */\n    @Override\n    public ExchangeRate getExchangeNode(final String rateId) {\n        ExchangeRate exchangeRate = null;\n\n        for (final ExchangeRate rate : query(ExchangeRate.class)) {\n            if (rate.getRateId().equals(rateId)) {\n                exchangeRate = rate;\n                break;\n            }\n        }\n\n        return exchangeRate;\n    }\n\n    @Override\n    public ExchangeRate getExchangeRateByUuid(final UUID uuid) {\n        return getObjectByUuid(ExchangeRate.class, uuid);\n    }\n\n    @Override\n    public SecurityNode getSecurityByUuid(final UUID uuid) {\n        return getObjectByUuid(SecurityNode.class, uuid);\n    }\n\n    /*\n     * @see jgnash.engine.dao.CommodityDAO#getSecurities()\n     */\n    @Override\n    @NotNull\n    public List<SecurityNode> getSecurities() {\n        return query(SecurityNode.class);\n    }\n\n    @Override\n    public List<ExchangeRate> getExchangeRates() {\n        return query(ExchangeRate.class);\n    }\n\n    /*\n     * @see jgnash.engine.CommodityDAOInterface#setExchangeRate(jgnash.engine.ExchangeRate)\n     */\n    @Override\n    public void addExchangeRate(final ExchangeRate eRate) {\n        persist(eRate);\n    }\n\n    /*\n     * @see jgnash.engine.CommodityDAOInterface#updateCommodityNode(jgnash.engine.CommodityNode)\n     */\n    @Override\n    public boolean updateCommodityNode(final CommodityNode node) {\n        return merge(node) != null;\n    }\n\n    /*\n     * @see jgnash.engine.CommodityDAOInterface#getActiveAccountCommodities()\n     */\n    @Override\n    public Set<CurrencyNode> getActiveCurrencies() {\n        Set<CurrencyNode> currencyNodeSet = Collections.emptySet();\n\n        try {\n            Future<Set<CurrencyNode>> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final TypedQuery<Account> q = em.createQuery(\"SELECT a FROM Account a WHERE a.markedForRemoval = false\",\n                            Account.class);\n\n                    final List<Account> accountList = q.getResultList();\n                    final Set<CurrencyNode> currencies = new HashSet<>();\n\n                    for (final Account account : accountList) {\n                        currencies.add(account.getCurrencyNode());\n\n                        currencies.addAll(account.getSecurities().parallelStream()\n                                                  .map(SecurityNode::getReportedCurrencyNode).collect(Collectors.toList()));\n                    }\n\n                    return currencies;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            currencyNodeSet = future.get();\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return currencyNodeSet;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaConfigDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\nimport javax.persistence.criteria.Root;\n\nimport jgnash.engine.Config;\nimport jgnash.engine.dao.ConfigDAO;\n\n/**.\n * Config DAO\n *\n * @author Craig Cavanaugh\n */\nclass JpaConfigDAO extends AbstractJpaDAO implements ConfigDAO {\n\n    private static final Logger logger = Logger.getLogger(JpaConfigDAO.class.getName());\n\n    JpaConfigDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    /*\n     * @see jgnash.engine.ConfigDAOInterface#getDefaultConfig()\n     */\n    @Override\n    public synchronized Config getDefaultConfig() {\n        Config defaultConfig = null;\n\n        try {\n            Future<Config> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    Config newConfig;\n                    try {\n                        final CriteriaBuilder cb = em.getCriteriaBuilder();\n                        final CriteriaQuery<Config> cq = cb.createQuery(Config.class);\n                        final Root<Config> root = cq.from(Config.class);\n                        cq.select(root);\n\n                        final TypedQuery<Config> q = em.createQuery(cq);\n\n                        newConfig = q.getSingleResult();\n\n                    } catch (final Exception e) {\n                        newConfig = new Config();\n\n                        em.getTransaction().begin();\n                        em.persist(newConfig);\n                        em.getTransaction().commit();\n\n                        logger.info(\"Generating new default config\");\n                    }\n                    return newConfig;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            defaultConfig = future.get();\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return defaultConfig;\n    }\n\n    @Override\n    public void update(final Config config) {\n        persist(config);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaConfiguration.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.util.FileUtils;\n\n/**\n * Utility class to help with JPA configuration.\n *\n * @author Craig Cavanaugh\n */\nclass JpaConfiguration {\n\n    private JpaConfiguration() {\n        // utility class\n    }\n\n    static final String UNIT_NAME = \"jgnash\";\n    static final String DEFAULT_USER = \"JGNASH\";\n\n    static final String JAVAX_PERSISTENCE_JDBC_URL = \"javax.persistence.jdbc.url\";\n\n    private static final String JAVAX_PERSISTENCE_JDBC_DRIVER = \"javax.persistence.jdbc.driver\";\n    private static final String JAVAX_PERSISTENCE_JDBC_USER = \"javax.persistence.jdbc.user\";\n    private static final String JAVAX_PERSISTENCE_JDBC_PASSWORD = \"javax.persistence.jdbc.password\";\n    private static final String HIBERNATE_DIALECT = \"hibernate.dialect\";\n    private static final String HIBERNATE_HBM2DDL_AUTO = \"hibernate.hbm2ddl.auto\";\n\n    private static final String UNKNOWN_DATABASE_TYPE = \"Unknown database type\";\n\n    private static Properties getBaseProperties(final DataStoreType database) {\n        Properties properties = System.getProperties();\n\n        properties.setProperty(HIBERNATE_HBM2DDL_AUTO, \"update\");\n\n        switch (database) {\n            case H2_DATABASE:\n            case H2MV_DATABASE:\n                properties.setProperty(JAVAX_PERSISTENCE_JDBC_DRIVER, \"org.h2.Driver\");\n                properties.setProperty(HIBERNATE_DIALECT, \"org.hibernate.dialect.H2Dialect\");\n                break;\n            case HSQL_DATABASE:\n                properties.setProperty(JAVAX_PERSISTENCE_JDBC_DRIVER, \"org.hsqldb.jdbc.JDBCDriver\");\n                properties.setProperty(HIBERNATE_DIALECT, \"org.hibernate.dialect.HSQLDialect\");\n                break;\n            default:\n                throw new RuntimeException(UNKNOWN_DATABASE_TYPE);\n        }\n\n        return properties;\n    }\n\n    static Properties getLocalProperties(final DataStoreType dataStoreType, final String fileName, final char[] password,\n                                         final boolean readOnly) {\n        Objects.requireNonNull(password);\n        Objects.requireNonNull(dataStoreType);\n\n        final StringBuilder urlBuilder = new StringBuilder();\n\n        switch (dataStoreType) {\n            case H2_DATABASE:\n            case H2MV_DATABASE: //\n                //urlBuilder.append(\"jdbc:h2:nio:\");\n                urlBuilder.append(\"jdbc:h2:async:\");\n\n                urlBuilder.append(FileUtils.stripFileExtension(fileName));\n\n                urlBuilder.append(\";USER=\").append(DEFAULT_USER);\n\n                if (password.length > 0) {\n                    urlBuilder.append(\";PASSWORD=\").append(password);\n                }\n\n                // use the old 1.3 page storage format instead of the MVStore based on file extension.  This allows\n                // for correct handling of old files without forcing an upgrade\n                if (fileName.endsWith(JpaH2DataStore.H2_FILE_EXT)) {\n                    urlBuilder.append(\";MV_STORE=FALSE\");\n                } else {\n                    urlBuilder.append(\";COMPRESS=TRUE;FILE_LOCK=FILE\");   // do not use FS locking for\n                }\n\n                if (readOnly) {\n                    urlBuilder.append(\";ACCESS_MODE_DATA=r\");\n                }\n\n                urlBuilder.append(\";TRACE_LEVEL_SYSTEM_OUT=1\"); // make sure errors are logged to the console\n                break;\n            case HSQL_DATABASE:\n                urlBuilder.append(\"jdbc:hsqldb:file:\");\n                urlBuilder.append(FileUtils.stripFileExtension(fileName));\n\n                urlBuilder.append(\";user=\").append(DEFAULT_USER);\n\n                if (password.length > 0) {\n                    urlBuilder.append(\";password=\").append(password);\n                }\n\n                if (readOnly) {\n                    urlBuilder.append(\";readonly=true\");\n                }\n                break;\n            default:\n                throw new RuntimeException(UNKNOWN_DATABASE_TYPE);\n        }\n\n        final Properties properties = getBaseProperties(dataStoreType);\n\n        properties.setProperty(JAVAX_PERSISTENCE_JDBC_URL, urlBuilder.toString());\n        properties.setProperty(JAVAX_PERSISTENCE_JDBC_USER, DEFAULT_USER);\n        properties.setProperty(JAVAX_PERSISTENCE_JDBC_PASSWORD, new String(password));\n\n        return properties;\n    }\n\n    /**\n     * Generates and a JPA properties to connect to a remote database.\n     *\n     * @param dataStoreType DataStoreType type\n     * @param fileName remote file to connect to, ignored for HSQL_DATABASE connections\n     * @param host remote host\n     * @param port remote port\n     * @param password database password\n     * @return   JPA properties\n     */\n    static Properties getClientProperties(final DataStoreType dataStoreType, final String fileName, final String host,\n                                          final int port, final char[] password) {\n        Objects.requireNonNull(password);\n        Objects.requireNonNull(dataStoreType);\n\n        final StringBuilder urlBuilder = new StringBuilder();\n\n        final Properties properties = getBaseProperties(dataStoreType);\n\n        switch (dataStoreType) {\n            case H2_DATABASE:\n            case H2MV_DATABASE:\n                urlBuilder.append(\"jdbc:h2\");\n\n                /*boolean useSSL = Boolean.parseBoolean(properties.getProperty(EncryptionManager.ENCRYPTION_FLAG));\n                if (useSSL) {\n                    urlBuilder.append(\":ssl://\");\n                } else {\n                    urlBuilder.append(\":tcp://\");\n                }*/\n\n                urlBuilder.append(\":tcp://\");\n\n                urlBuilder.append(host).append(\":\").append(port).append(\"/\");\n                urlBuilder.append(FileUtils.stripFileExtension(fileName));\n\n                urlBuilder.append(\";USER=\").append(DEFAULT_USER);\n                urlBuilder.append(\";PASSWORD=\").append(password);\n                break;\n            case HSQL_DATABASE:\n                urlBuilder.append(\"jdbc:hsqldb:hsql://\");\n                urlBuilder.append(host).append(\":\").append(port).append(\"/jgnash\"); // needs a public alias\n\n                urlBuilder.append(\";user=\").append(DEFAULT_USER);\n\n                if (password.length > 0) {\n                    urlBuilder.append(\";password=\").append(password);\n                }\n\n                Logger.getLogger(JpaConfiguration.class.getName()).info(urlBuilder.toString());\n                break;\n            default:\n                throw new RuntimeException(UNKNOWN_DATABASE_TYPE);\n        }\n\n        properties.setProperty(JAVAX_PERSISTENCE_JDBC_USER, DEFAULT_USER);\n        properties.setProperty(JAVAX_PERSISTENCE_JDBC_PASSWORD, new String(password));\n        properties.setProperty(JAVAX_PERSISTENCE_JDBC_URL, urlBuilder.toString());\n\n        return properties;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaEngineDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\nimport javax.persistence.EntityManager;\n\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.dao.AccountDAO;\nimport jgnash.engine.dao.BudgetDAO;\nimport jgnash.engine.dao.CommodityDAO;\nimport jgnash.engine.dao.ConfigDAO;\nimport jgnash.engine.dao.EngineDAO;\nimport jgnash.engine.dao.RecurringDAO;\nimport jgnash.engine.dao.TagDAO;\nimport jgnash.engine.dao.TransactionDAO;\nimport jgnash.engine.dao.TrashDAO;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Engine DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaEngineDAO extends AbstractJpaDAO implements EngineDAO {\n\n    private AccountDAO accountDAO;\n\n    private BudgetDAO budgetDAO;\n\n    private CommodityDAO commodityDAO;\n\n    private ConfigDAO configDAO;\n\n    private RecurringDAO recurringDAO;\n\n    private TagDAO tagDAO;\n\n    private TransactionDAO transactionDAO;\n\n    private TrashDAO trashDAO;\n\n    JpaEngineDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    @Override\n    public synchronized void shutdown() {\n\n        emLock.lock();\n\n        try {\n            // Stop the trash executor service\n            ((JpaTrashDAO) getTrashDAO()).stopTrashExecutor();\n\n            // Stop the shared executor service, wait for all tasks to complete and reset\n            shutDownExecutor();\n        } finally {\n            emLock.unlock();\n        }\n    }\n\n    @Override\n    public synchronized AccountDAO getAccountDAO() {\n        if (accountDAO == null) {\n            accountDAO = new JpaAccountDAO(em, isRemote);\n        }\n        return accountDAO;\n    }\n\n    @Override\n    public BudgetDAO getBudgetDAO() {\n        if (budgetDAO == null) {\n            budgetDAO = new JpaBudgetDAO(em, isRemote);\n        }\n        return budgetDAO;\n    }\n\n    @Override\n    public synchronized CommodityDAO getCommodityDAO() {\n        if (commodityDAO == null) {\n            commodityDAO = new JpaCommodityDAO(em, isRemote);\n        }\n        return commodityDAO;\n    }\n\n    @Override\n    public synchronized ConfigDAO getConfigDAO() {\n        if (configDAO == null) {\n            configDAO = new JpaConfigDAO(em, isRemote);\n        }\n        return configDAO;\n    }\n\n    @Override\n    public synchronized RecurringDAO getRecurringDAO() {\n        if (recurringDAO == null) {\n            recurringDAO = new JpaRecurringDAO(em, isRemote);\n        }\n        return recurringDAO;\n    }\n\n    @Override\n    public TagDAO getTagDAO() {\n        if (tagDAO == null) {\n            tagDAO = new JpaTagDAO(em, isRemote);\n        }\n        return tagDAO;\n    }\n\n    @Override\n    public synchronized TransactionDAO getTransactionDAO() {\n        if (transactionDAO == null) {\n            transactionDAO = new JpaTransactionDAO(em, isRemote);\n        }\n        return transactionDAO;\n    }\n\n    @Override\n    public synchronized TrashDAO getTrashDAO() {\n        if (trashDAO == null) {\n            trashDAO = new JpaTrashDAO(em, isRemote);\n        }\n        return trashDAO;\n    }\n\n    @Override\n    public List<StoredObject> getStoredObjects() {\n        return query(StoredObject.class);\n    }\n\n    @Override\n    public <T extends StoredObject> List<T> getStoredObjects(final Class<T> tClass) {\n        return query(tClass);\n    }\n\n    /**\n     * Refresh a managed object.\n     *\n     * @param object object to re\n     */\n    @Override\n    public void refresh(final StoredObject object) {\n        try {\n            Future<Void> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.refresh(object);\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            future.get();   // block\n        } catch (ExecutionException | InterruptedException e) {\n            logSevere(JpaEngineDAO.class, e);\n        }\n    }\n\n    @Override\n    public void bulkUpdate(final List<? extends StoredObject> objectList) {\n        try {\n            final Future<Void> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n                    objectList.forEach(em::persist);\n                    em.getTransaction().commit();\n\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            future.get();   // block\n        } catch (final InterruptedException | ExecutionException e) {\n            logSevere(JpaEngineDAO.class, e);\n        }\n    }\n\n    @Override\n    public boolean isRemote() {\n        return isRemote;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaH2DataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.logging.Level;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\n/**\n * JPA specific code for data storage and creating an engine.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaH2DataStore extends AbstractJpaDataStore {\n\n    public static final String H2_FILE_EXT = \".h2.db\";\n\n    public static final String LOCK_EXT = \".lock.db\";\n\n    @NotNull\n    @Override\n    public String getFileExt() {\n        return H2_FILE_EXT;\n    }\n\n    @Override\n    public DataStoreType getType() {\n        return DataStoreType.H2_DATABASE;\n    }\n\n    @Override\n    protected String getLockFileExtension() {\n        return LOCK_EXT;\n    }\n\n    @Override\n    public void deleteDatabase(final String fileName) {\n        final String[] extensions = new String[]{getFileExt(), getLockFileExtension()};\n\n        final String base = FileUtils.stripFileExtension(fileName);\n\n        for (final String extension : extensions) {\n            try {\n                logger.log(Level.INFO, \"Deleting {0}{1}\", new Object[]{base, extension});\n                Files.deleteIfExists(Paths.get(base + extension));\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getMessage(), e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaH2MvDataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.util.NotNull;\n\n/**\n * JPA specific code for data storage and creating an engine.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaH2MvDataStore extends JpaH2DataStore {\n\n    public static final String MV_FILE_EXT = \".mv.db\";\n\n    @NotNull\n    @Override\n    public String getFileExt() {\n        return MV_FILE_EXT;\n    }\n\n    @Override\n    public DataStoreType getType() {\n        return DataStoreType.H2MV_DATABASE;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaHsqlDataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.logging.Level;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\n/**\n * JPA specific code for HSQLDB data storage and creating an engine.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaHsqlDataStore extends AbstractJpaDataStore {\n\n    public static final String FILE_EXT = \".script\";\n\n    public static final String LOCK_EXT = \".lck\";\n\n    private static final String[] extensions = new String[]{\".log\", \".properties\", FILE_EXT, \".data\", \".backup\",\n            \".tmp\", \".lobs\", LOCK_EXT};\n\n    @NotNull\n    @Override\n    public String getFileExt() {\n        return FILE_EXT;\n    }\n\n    @Override\n    public DataStoreType getType() {\n        return DataStoreType.HSQL_DATABASE;\n    }\n\n    @Override\n    public void rename(final String fileName, final String newFileName) throws IOException {\n\n        for (final String extension : extensions) {\n            final Path path = Paths.get(FileUtils.stripFileExtension(fileName) + extension);\n\n            if (Files.exists(path)) {\n                Files.move(path, Paths.get(FileUtils.stripFileExtension(newFileName) + extension));\n            }\n        }\n    }\n\n    @Override\n    public String getLockFileExtension() {\n        return LOCK_EXT;\n    }\n\n    @Override\n    public void deleteDatabase(final String fileName) {\n\n        final String base = FileUtils.stripFileExtension(fileName);\n\n        for (final String extension : extensions) {\n            try {\n                Files.deleteIfExists(Paths.get(base + extension));\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getMessage(), e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaNetworkServer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.EntityManagerFactory;\nimport javax.persistence.Persistence;\n\nimport jgnash.engine.AttachmentUtils;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineException;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.attachment.AttachmentTransferServer;\nimport jgnash.engine.attachment.DistributedAttachmentManager;\nimport jgnash.engine.concurrent.DistributedLockManager;\nimport jgnash.engine.concurrent.DistributedLockServer;\nimport jgnash.engine.message.LocalServerListener;\nimport jgnash.engine.message.MessageBusServer;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.FileMagic;\nimport jgnash.util.FileUtils;\nimport jgnash.util.LogUtil;\n\n/**\n * JPA network server.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaNetworkServer {\n\n    public static final String STOP_SERVER_MESSAGE = \"<STOP_SERVER>\";\n\n    public static final int MESSAGE_SERVER_INCREMENT = 1;\n\n    static final int LOCK_SERVER_INCREMENT = 2;\n\n    static final int TRANSFER_SERVER_INCREMENT = 3;\n\n    private final CountDownLatch stopLatch = new CountDownLatch(1);\n\n    private static final int BACKUP_PERIOD = 2;\n\n    private volatile boolean dirty = false;\n\n    private EntityManager em;\n\n    private EntityManagerFactory factory;\n\n    private DistributedLockManager distributedLockManager;\n\n    private DistributedAttachmentManager distributedAttachmentManager;\n\n    public static final int DEFAULT_PORT = 5300;\n\n    private static final String SERVER_ENGINE = \"server\";\n\n    private static final Logger logger = Logger.getLogger(JpaNetworkServer.class.getName());\n\n    private Runnable runningCallback = null;\n\n    public synchronized void startServer(final String fileName, final int port, final char[] password) throws EngineException {\n\n        final Path file = Paths.get(fileName);\n\n        // create the base directory if needed\n        if (!Files.exists(file)) {\n            final Path parent = file.getParent();\n\n            if (parent != null && !Files.exists(parent)) {\n                try {\n                    Files.createDirectories(parent);\n                } catch (IOException e) {\n                    throw new EngineException(\"Could not create directory for file: \" + parent);\n                }\n            }\n        }\n\n        final FileMagic.FileType type = FileMagic.magic(Paths.get(fileName));\n\n        switch (type) {\n            case h2:\n            case h2mv:\n                runH2Server(fileName, port, password);\n                break;\n            case hsql:\n                runHsqldbServer(fileName, port, password);\n                break;\n            default:\n                logger.severe(\"Not a valid file type for server usage\");\n        }\n\n        System.exit(0); // force exit\n    }\n\n    public synchronized void startServer(final String fileName, final int port, final char[] password,\n                                         final Runnable callback) throws EngineException {\n        this.runningCallback = callback;\n        this.startServer(fileName, port, password);\n    }\n\n    /**\n     * Starts the server and blocks until it is stopped\n     *\n     * @param dataStoreType datastore type\n     * @param fileName      database file name\n     * @param port          port\n     * @param password      password*\n     * @throws EngineException thrown if engine or buss cannot be created\n     */\n    private synchronized void run(final DataStoreType dataStoreType, final String fileName, final int port,\n                                  final char[] password) throws EngineException {\n\n        final DistributedLockServer distributedLockServer = new DistributedLockServer(port + LOCK_SERVER_INCREMENT);\n        final boolean lockServerStarted = distributedLockServer.startServer(password);\n\n        final AttachmentTransferServer attachmentTransferServer\n                = new AttachmentTransferServer(port + TRANSFER_SERVER_INCREMENT,\n                AttachmentUtils.getAttachmentDirectory(Paths.get(fileName)));\n        final boolean attachmentServerStarted = attachmentTransferServer.startServer(password);\n\n        if (attachmentServerStarted && lockServerStarted) {\n            final Engine engine = createEngine(dataStoreType, fileName, port, password);\n\n            if (engine != null) {\n\n                // Start the message bus and pass the file name so it can be reported to the client\n                final MessageBusServer messageBusServer = new MessageBusServer(port + MESSAGE_SERVER_INCREMENT);\n\n                // don't continue if the server is not started successfully\n                if (messageBusServer.startServer(dataStoreType, fileName, password)) {\n                    // Start the backup thread that ensures an XML backup is created at set intervals\n                    final ScheduledExecutorService backupExecutor\n                            = Executors.newSingleThreadScheduledExecutor(\n                            new DefaultDaemonThreadFactory(\"JPA Network Server Executor\"));\n\n                    // run commit every backup period after startup\n                    backupExecutor.scheduleWithFixedDelay(() -> {\n                        if (dirty) {\n                            exportXML(engine, fileName);\n                            EngineFactory.removeOldCompressedXML(fileName, engine.getRetainedBackupLimit());\n                            dirty = false;\n                        }\n                    }, BACKUP_PERIOD, BACKUP_PERIOD, TimeUnit.HOURS);\n\n                    final LocalServerListener listener = event -> {\n\n                        // look for a remote request to stop the server\n                        if (event.startsWith(STOP_SERVER_MESSAGE)) {\n                            logger.info(\"Remote shutdown request was received\");\n                            stopServer();\n                        }\n\n                        dirty = true;\n                    };\n\n                    messageBusServer.addLocalListener(listener);\n\n                    // if a callback has been registered, call it\n                    if (runningCallback != null) {\n                        runningCallback.run();\n                    }\n\n                    // wait here forever\n                    try {\n                        while (stopLatch.getCount() != 0) { // check for condition, handle a spurious wake up\n                            stopLatch.await();   // wait forever from stopServer()\n                        }\n                    } catch (final InterruptedException ex) {\n                        logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n                        Thread.currentThread().interrupt();\n                    }\n\n                    messageBusServer.removeLocalListener(listener);\n\n                    backupExecutor.shutdown();\n\n                    exportXML(engine, fileName);\n\n                    messageBusServer.stopServer();\n\n                    EngineFactory.closeEngine(SERVER_ENGINE);\n\n                    EngineFactory.removeOldCompressedXML(fileName, engine.getRetainedBackupLimit());\n\n                    distributedLockManager.disconnectFromServer();\n                    distributedAttachmentManager.disconnectFromServer();\n\n                    distributedLockServer.stopServer();\n                    attachmentTransferServer.stopServer();\n\n                    em.close();\n\n                    factory.close();\n                } else {\n                    throw new EngineException(\"Failed to start the Message Bus\");\n                }\n            } else {\n                throw new EngineException(\"Failed to create the engine\");\n            }\n        } else {\n            if (lockServerStarted) {\n                distributedLockServer.stopServer();\n            }\n\n            if (attachmentServerStarted) {\n                attachmentTransferServer.stopServer();\n            }\n        }\n    }\n\n    private void runH2Server(final String fileName, final int port, final char[] password) {\n        org.h2.tools.Server server = null;\n\n        try {\n            final List<String> serverArgs = new ArrayList<>();\n\n            serverArgs.add(\"-tcpPort\");\n            serverArgs.add(String.valueOf(port));\n            serverArgs.add(\"-tcpAllowOthers\");\n\n            /*boolean useSSL = Boolean.parseBoolean(System.getProperties().getProperty(EncryptionManager.ENCRYPTION_FLAG));\n\n            if (useSSL) {\n                serverArgs.add(\"-tcpSSL\");\n            }*/\n\n            server = org.h2.tools.Server.createTcpServer(serverArgs.toArray(new String[0]));\n            server.start();\n\n        } catch (final SQLException e) {\n            logger.log(Level.SEVERE, e.getMessage(), e);\n        }\n\n        // Start the message server and engine, this should block until closed\n        try {\n            run(DataStoreType.H2_DATABASE, fileName, port, password);\n        } catch (final EngineException e) {\n            LogUtil.logSevere(JpaNetworkServer.class, e);\n        }\n\n        if (server != null) {\n            server.stop();\n        }\n    }\n\n    private void runHsqldbServer(final String fileName, final int port, final char[] password) {\n        org.hsqldb.server.Server hsqlServer = new org.hsqldb.server.Server();\n\n        hsqlServer.setPort(port);\n        hsqlServer.setDatabaseName(0, JpaConfiguration.UNIT_NAME);    // the alias\n        hsqlServer.setDatabasePath(0, \"file:\" + FileUtils.stripFileExtension(fileName));\n\n        hsqlServer.start();\n\n        // Start the message server and engine, this should block until closed\n        try {\n            run(DataStoreType.HSQL_DATABASE, fileName, port, password);\n        } catch (final EngineException e) {\n            LogUtil.logSevere(JpaNetworkServer.class, e);\n        }\n\n        hsqlServer.stop();\n    }\n\n    /**\n     * stops this server.\n     */\n    private synchronized void stopServer() {\n        stopLatch.countDown();  // will result in zero which will trigger a stop\n    }\n\n    private Engine createEngine(final DataStoreType dataStoreType, final String fileName, final int port,\n                                final char[] password) {\n\n        final Properties properties = JpaConfiguration.getClientProperties(dataStoreType, fileName, EngineFactory.LOCALHOST, port,\n                password);\n\n        logger.log(Level.INFO, \"Local connection url is: {0}\",\n                properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL));\n\n        Engine engine = null;\n\n        try {\n            // An exception will be thrown if the password is not correct, or the database did not have a password\n            if (SqlUtils.isConnectionValid(properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL))) {\n\n                /* specifies the unit name and properties.  Unit name can be used to specify a different persistence\n                   unit defined in persistence.xml */\n                factory = Persistence.createEntityManagerFactory(JpaConfiguration.UNIT_NAME, properties);\n                em = factory.createEntityManager();\n\n                distributedLockManager = new DistributedLockManager(EngineFactory.LOCALHOST, port + LOCK_SERVER_INCREMENT);\n                distributedLockManager.connectToServer(password);\n\n                distributedAttachmentManager = new DistributedAttachmentManager(EngineFactory.LOCALHOST, port + TRANSFER_SERVER_INCREMENT);\n                distributedAttachmentManager.connectToServer(password);\n\n                logger.info(\"Created local JPA container and engine\");\n\n                engine = new Engine(new JpaEngineDAO(em, true), distributedLockManager, distributedAttachmentManager,\n                        SERVER_ENGINE); // treat as a remote engine\n            }\n        } catch (final Exception e) {\n            logger.log(Level.SEVERE, e.toString(), e);\n        }\n\n        return engine;\n    }\n\n    private static void exportXML(final Engine engine, final String fileName) {\n        ArrayList<StoredObject> list = new ArrayList<>(engine.getStoredObjects());\n\n        EngineFactory.exportCompressedXML(fileName, list);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaRecurringDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\n\nimport java.util.List;\nimport java.util.UUID;\n\nimport javax.persistence.EntityManager;\n\nimport jgnash.engine.dao.RecurringDAO;\nimport jgnash.engine.recurring.Reminder;\n\n/**\n * JPA Reminder DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaRecurringDAO extends AbstractJpaDAO implements RecurringDAO {\n\n    JpaRecurringDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    /*\n     * @see jgnash.engine.ReminderDAOInterface#getReminderList()\n     */\n    @Override\n    public List<Reminder> getReminderList() {\n        return query(Reminder.class);\n    }\n\n    /*\n     * @see jgnash.engine.ReminderDAOInterface#addReminder(jgnash.engine.recurring.Reminder)\n     */\n    @Override\n    public boolean addReminder(final Reminder reminder) {\n        return persist(reminder);\n    }\n\n    @Override\n    public Reminder getReminderByUuid(final UUID uuid) {\n        return getObjectByUuid(Reminder.class, uuid);\n    }\n\n\n    @Override\n    public boolean updateReminder(final Reminder reminder) {\n        return addReminder(reminder);   // call add, same code\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaTagDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.persistence.EntityManager;\n\nimport jgnash.engine.Tag;\nimport jgnash.engine.dao.TagDAO;\n\n/**\n * Tag DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaTagDAO extends AbstractJpaDAO implements TagDAO {\n\n    JpaTagDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n    }\n\n    @Override\n    public boolean add(final Tag tag) {\n        return persist(tag);\n    }\n\n    @Override\n    public boolean update(final Tag tag) {\n        return merge(tag) != null;\n    }\n\n    @Override\n    public Set<Tag> getTags() {\n        return new HashSet<>(query(Tag.class));\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaTransactionDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\n\nimport jgnash.engine.Transaction;\nimport jgnash.engine.dao.TransactionDAO;\n\n/**\n * Transaction DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaTransactionDAO extends AbstractJpaDAO implements TransactionDAO {\n\n    private static final Logger logger = Logger.getLogger(JpaTransactionDAO.class.getName());\n\n    JpaTransactionDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n        logger.setLevel(Level.ALL);\n    }\n\n    /*\n     * @see jgnash.engine.dao.TransactionDAO#getTransactions()\n     */\n    @Override\n    public List<Transaction> getTransactions() {\n        return query(Transaction.class);\n    }\n\n    /*\n     * @see jgnash.engine.TransactionDAO#addTransaction(jgnash.engine.Transaction)\n     */\n    @Override\n    public synchronized boolean addTransaction(final Transaction transaction) {\n        boolean result = false;\n\n        try {\n            final Future<Boolean> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    em.persist(transaction);\n                    transaction.getAccounts().forEach(em::persist);\n\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return true;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            result = future.get();  // block and return\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return result;\n    }\n\n    @Override\n    public Transaction getTransactionByUuid(final UUID uuid) {\n        return getObjectByUuid(Transaction.class, uuid);\n    }\n\n    /*\n     * @see jgnash.engine.TransactionDAO#removeTransaction(jgnash.engine.Transaction)\n     */\n    @Override\n    public synchronized boolean removeTransaction(final Transaction transaction) {\n        boolean result = false;\n\n        try {\n            final Future<Boolean> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    // look at accounts this transaction impacted and update the accounts\n                    transaction.getAccounts().forEach(em::persist);\n\n                    em.persist(transaction);    // saved, removed with the trash\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return true;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            result = future.get();  // block and return\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return result;\n    }\n\n    @Override\n    public List<Transaction> getTransactionsWithAttachments() {\n        List<Transaction> transactionList = Collections.emptyList();\n\n        try {\n            final Future<List<Transaction>> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final TypedQuery<Transaction> q = em\n                            .createQuery(\"SELECT t FROM Transaction t WHERE t.markedForRemoval = false AND t.attachment is not null\",\n                                    Transaction.class);\n\n                    return new ArrayList<>(q.getResultList());\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            transactionList = future.get(); // block and return\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return transactionList;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaTrashDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.time.LocalDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.EntityManager;\nimport javax.persistence.TypedQuery;\nimport javax.persistence.criteria.CriteriaBuilder;\nimport javax.persistence.criteria.CriteriaQuery;\nimport javax.persistence.criteria.Root;\n\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.TrashObject;\nimport jgnash.engine.concurrent.Priority;\nimport jgnash.engine.dao.TrashDAO;\nimport jgnash.util.DefaultDaemonThreadFactory;\n\nimport org.apache.commons.collections4.ListUtils;\n\n/**\n * JPA Trash DAO.\n *\n * @author Craig Cavanaugh\n */\nclass JpaTrashDAO extends AbstractJpaDAO implements TrashDAO {\n\n    private static final Logger logger = Logger.getLogger(JpaTrashDAO.class.getName());\n\n    private static final long MAXIMUM_ENTITY_TRASH_AGE = 2000;\n\n    private static final int INITIAL_DELAY = 60;    // Delay start 60 seconds\n\n    private static final int PERIOD = 35;   // Execute every 35 seconds\n\n    private static final int MAX_ENTITY_LUMP = 5;\n\n    private ScheduledExecutorService entityTrashExecutor;\n\n    JpaTrashDAO(final EntityManager entityManager, final boolean isRemote) {\n        super(entityManager, isRemote);\n\n        entityTrashExecutor = Executors.newSingleThreadScheduledExecutor(\n                new DefaultDaemonThreadFactory(\"JPA Trash Executor\"));\n\n        // run trash cleanup every 35 seconds 1 minute after startup\n        entityTrashExecutor.scheduleWithFixedDelay(JpaTrashDAO.this::cleanupEntityTrash, INITIAL_DELAY, PERIOD,\n                TimeUnit.SECONDS);\n    }\n\n    void stopTrashExecutor() {\n        entityTrashExecutor.shutdown();\n\n        try {\n            entityTrashExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);\n            entityTrashExecutor = null;\n        } catch (InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    @Override\n    public List<TrashObject> getTrashObjects() {\n        List<TrashObject> trashObjectList = Collections.emptyList();\n\n        try {\n            final Future<List<TrashObject>> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final CriteriaBuilder cb = em.getCriteriaBuilder();\n                    final CriteriaQuery<TrashObject> cq = cb.createQuery(TrashObject.class);\n                    final Root<TrashObject> root = cq.from(TrashObject.class);\n                    cq.select(root);\n\n                    final TypedQuery<TrashObject> q = em.createQuery(cq);\n\n                    return new ArrayList<>(q.getResultList());\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            trashObjectList = future.get(); // block\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        return trashObjectList;\n    }\n\n    @Override\n    public void add(final TrashObject trashObject) {\n        try {\n            final Future<Void> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    em.persist(trashObject.getObject());\n                    em.persist(trashObject);\n\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            future.get();   // block\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    @Override\n    public void remove(final TrashObject trashObject) {\n        try {\n            final Future<Void> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    final StoredObject object = trashObject.getObject();\n\n                    em.remove(object);\n                    em.remove(trashObject);\n\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    logger.info(\"Removed TrashObject\");\n\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            }, Priority.BACKGROUND);\n\n            future.get(); // block\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    @Override\n    public void addEntityTrash(final Object entity) {\n        try {\n            final Future<Void> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    em.getTransaction().begin();\n\n                    em.persist(entity);\n                    em.persist(new JpaTrashEntity(entity));\n\n                    em.getTransaction().commit();\n\n                    dirtyFlag.set(true);\n\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            });\n\n            future.get();   // block\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private void cleanupEntityTrash() {\n        try {\n            final Future<Void> future = executorService.submit(() -> {\n                emLock.lock();\n\n                try {\n                    final CriteriaBuilder cb = em.getCriteriaBuilder();\n                    final CriteriaQuery<JpaTrashEntity> cq = cb.createQuery(JpaTrashEntity.class);\n                    final Root<JpaTrashEntity> root = cq.from(JpaTrashEntity.class);\n                    cq.select(root);\n\n                    final TypedQuery<JpaTrashEntity> q = em.createQuery(cq);\n\n                    /* Partition the results into small chunks so other higher priority work can be performed without\n                       stalling the application */\n                    final List<List<JpaTrashEntity>> listList = ListUtils.partition(q.getResultList(), MAX_ENTITY_LUMP);\n\n                    for (final List<JpaTrashEntity> entityList : listList) {\n\n                        executorService.submit(() -> {\n                            emLock.lock();\n\n                            try {\n                                em.getTransaction().begin();\n\n                                for (final JpaTrashEntity trashEntity : entityList) {\n                                    if (!trashEntity.isPending()\n                                            && ChronoUnit.MILLIS.between(trashEntity.getDate(), LocalDateTime.now())\n                                            >= MAXIMUM_ENTITY_TRASH_AGE) {\n\n                                        trashEntity.setPending();\n\n                                        final Class<?> clazz = Class.forName(trashEntity.getClassName());\n                                        final Object entity = em.find(clazz, trashEntity.getEntityId());\n\n                                        if (entity != null) {\n                                            em.remove(entity);\n                                            logger.log(Level.INFO, \"Removed entity trash: {0}@{1}\",\n                                                    new Object[]{trashEntity.getClassName(), trashEntity.getEntityId()});\n                                        }\n                                        em.remove(trashEntity);\n                                    }\n                                }\n\n                                em.getTransaction().commit();\n                            } finally {\n                                emLock.unlock();\n                            }\n\n                            return null;\n                        }, Priority.BACKGROUND);\n                    }\n\n                    return null;\n                } finally {\n                    emLock.unlock();\n                }\n            }, Priority.BACKGROUND);\n\n            future.get();       // block\n        } catch (final InterruptedException | ExecutionException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/JpaTrashEntity.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.SequenceGenerator;\nimport java.lang.reflect.Field;\nimport java.time.LocalDateTime;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * A Trash entity for generic cleanup of typed entities that need to be cleanup up\n * at a later date because of how JPA operates, but the object is not a StoredObject.\n *\n * @author Craig Cavanaugh\n */\n@Entity\n@SequenceGenerator(name = \"sequence\", allocationSize = 10)\npublic class JpaTrashEntity {\n\n    @SuppressWarnings(\"unused\")\n    @Id\n    @GeneratedValue(generator = \"sequence\", strategy = GenerationType.SEQUENCE)\n    private long id;\n\n    private long entityId;\n\n    /**\n     * Date object was added.\n     */\n    private final LocalDateTime date = LocalDateTime.now();\n\n    private String className;\n\n    /**\n     * Will be true if it has been scheduled for removal.  State is not persisted should the engine be close prior\n     * to complete removal.\n     */\n    private transient boolean pending = false;\n\n    /**\n     * No argument constructor for reflection purposes.\n     * <b>Do not use to create a new instance</b>\n     */\n    @SuppressWarnings(\"unused\")\n    protected JpaTrashEntity() {\n\n    }\n\n    JpaTrashEntity(final Object entity) {\n        className = entity.getClass().getName();\n        entityId = getBasicEntityId(entity);\n    }\n\n    public LocalDateTime getDate() {\n        return date;\n    }\n\n    String getClassName() {\n        return className;\n    }\n\n    long getEntityId() {\n        return entityId;\n    }\n\n    private static long getBasicEntityId(final Object object) {\n        for (final Field field : object.getClass().getDeclaredFields()) {\n            if (field.isAnnotationPresent(Id.class)) {\n                try {\n                    return field.getLong(object);\n                } catch (final IllegalAccessException e) {\n                    logSevere(JpaTrashEntity.class, e);\n                }\n            }\n        }\n        return -1;\n    }\n\n    void setPending() {\n        pending = true;\n    }\n\n    boolean isPending() {\n        return pending;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/jpa/SqlUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.jpa;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.EngineFactory;\nimport jgnash.util.FileUtils;\n\n/**\n * Utility class to perform low level SQL related stuff with the database.\n *\n * @author Craig Cavanaugh\n */\npublic class SqlUtils {\n\n    private static final Logger logger = Logger.getLogger(SqlUtils.class.getName());\n\n    /**\n     * Maximum amount of time to wait for the lock file to release after closure.  Typical time should be about 2\n     * seconds, but unit tests or large databases can sometimes take longer\n     */\n    private static final long MAX_LOCK_RELEASE_TIME = 30L * 1000L;\n\n    private static final int TABLE_NAME = 3;\n\n    private static final int COLUMN_NAME = 4;\n\n    private static final String SHUTDOWN = \"SHUTDOWN\";\n\n    private SqlUtils() {\n    }\n\n    public static boolean changePassword(final String fileName, final char[] password, final char[] newPassword) {\n        boolean result = false;\n\n        try {\n            if (!FileUtils.isFileLocked(fileName)) {\n                final DataStoreType dataStoreType = EngineFactory.getDataStoreByType(fileName);\n\n                Objects.requireNonNull(dataStoreType);\n\n                final Properties properties = JpaConfiguration.getLocalProperties(dataStoreType, fileName, password, false);\n                final String url = properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL);\n\n                try (final Connection connection = DriverManager.getConnection(url)) {\n\n                    try (final PreparedStatement statement = connection.prepareStatement(\"SET PASSWORD ?\")) {\n                        statement.setString(1, new String(newPassword));\n                        statement.execute();\n                        result = true;\n                    }\n                } catch (final SQLException e) {\n                    logger.log(Level.SEVERE, e.getMessage(), e);\n                }\n            }\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getMessage(), e);\n        }\n\n        return result;\n    }\n\n    /**\n     * Opens the database in readonly mode and reads the version of the file format.\n     *\n     * @param fileName name of file to open\n     * @param password connection password\n     * @return file version. Zero is returned if not found\n     */\n    public static float getFileVersion(final String fileName, final char[] password) {\n        float fileVersion = 0f;\n\n        try {\n            if (!FileUtils.isFileLocked(fileName)) {\n                final DataStoreType dataStoreType = EngineFactory.getDataStoreByType(fileName);\n                final Properties properties = JpaConfiguration.getLocalProperties(dataStoreType, fileName, password,\n                        false);\n                final String url = properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL);\n\n                try (final Connection connection = DriverManager.getConnection(url)) {\n                    try (final Statement statement = connection.createStatement()) {\n\n                        boolean configTableExists = false;\n\n                        // check for the existence of the table first\n                        final DatabaseMetaData metaData = connection.getMetaData();\n\n                        try (final ResultSet resultSet = metaData.getTables(null, null, \"CONFIG\", null)) {\n                            while (resultSet.next()) {\n                                if (resultSet.getString(TABLE_NAME).toUpperCase(Locale.ROOT).equals(\"CONFIG\")) {\n                                    configTableExists = true;\n                                }\n                            }\n                        }\n\n                        // check for the value if the table exists\n                        if (configTableExists) {\n                            try (final ResultSet resultSet = statement.executeQuery(\"SELECT FILEFORMAT FROM CONFIG\")) {\n                                resultSet.next();\n                                fileVersion = Float.parseFloat(resultSet.getString(\"fileformat\"));\n                            }\n                        }\n                    }\n\n                    // must issue a shutdown for correct file closure\n                    try (final Statement statement = connection.createStatement()) {\n                        statement.execute(SHUTDOWN);\n                    }\n                } catch (final SQLException e) {\n                    logger.log(Level.SEVERE, e.getMessage(), e);\n                }\n            } else {\n                logger.severe(\"File was locked\");\n            }\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getMessage(), e);\n        }\n\n        return fileVersion;\n    }\n\n    /**\n     * Diagnostic utility method to dump a list of table names and columns to the console.\n     * Assumes the file is not password protected.\n     *\n     * @param fileName name of file to open\n     * @return a {@code Set} of strings with the table names and columns, comma separated\n     */\n    public static Set<String> getTableAndColumnNames(final String fileName) {\n\n        final Set<String> tableNames = new TreeSet<>();\n\n        try {\n            if (!FileUtils.isFileLocked(fileName)) {\n                final DataStoreType dataStoreType = EngineFactory.getDataStoreByType(fileName);\n                final Properties properties = JpaConfiguration.getLocalProperties(dataStoreType, fileName,\n                        EngineFactory.EMPTY_PASSWORD, true);\n                final String url = properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL);\n\n                try (final Connection connection = DriverManager.getConnection(url)) {\n                    final DatabaseMetaData metaData = connection.getMetaData();\n\n                    try (final ResultSet resultSet = metaData.getColumns(null, null, \"%\", \"%\")) {\n                        while (resultSet.next()) {\n                            tableNames.add(resultSet.getString(TABLE_NAME).toUpperCase(Locale.ROOT) + \",\"\n                                    + resultSet.getString(COLUMN_NAME).toUpperCase(Locale.ROOT));\n                        }\n                    }\n\n                    // must issue a shutdown for correct file closure\n                    try (final Statement statement = connection.createStatement()) {\n                        statement.execute(SHUTDOWN);\n                    }\n                } catch (final SQLException e) {\n                    logger.log(Level.SEVERE, e.getMessage(), e);\n                }\n            } else {\n                logger.severe(\"File was locked\");\n            }\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getMessage(), e);\n        }\n\n        return tableNames;\n    }\n\n    @SuppressWarnings(\"SameParameterValue\")\n    static void dropColumn(final String fileName, final char[] password, final String table, final String... columns) {\n        try {\n            if (!FileUtils.isFileLocked(fileName)) {\n                final DataStoreType dataStoreType = EngineFactory.getDataStoreByType(fileName);\n                final Properties properties = JpaConfiguration.getLocalProperties(dataStoreType, fileName, password,\n                        false);\n                final String url = properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL);\n\n                try (final Connection connection = DriverManager.getConnection(url)) {\n\n                    final DatabaseMetaData metaData = connection.getMetaData();\n\n                    final ResultSet resultSet = metaData.getTables(null, null, table,\n                            new String[] {\"TABLE\"});\n\n                    while (resultSet.next()) {\n                        final String tableName = resultSet.getString(\"TABLE_NAME\");\n\n                        if (tableName !=null && tableName.equalsIgnoreCase(table)) {\n                            for (final String column : columns) {\n                                try (final Statement statement = connection.createStatement()) {\n                                    statement.execute(\"ALTER TABLE \" + table + \" DROP \" + column);\n                                }\n                            }\n                        }\n                    }\n\n                    // must issue a shutdown for correct file closure\n                    try (final Statement statement = connection.createStatement()) {\n                        statement.execute(SHUTDOWN);\n                    }\n                } catch (final SQLException e) {\n                    logger.log(Level.SEVERE, e.getMessage(), e);\n                }\n            } else {\n                logger.severe(\"File was locked\");\n            }\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getMessage(), e);\n        }\n    }\n\n    /**\n     * Returns true if the url is valid, throws an exception otherwise.\n     *\n     * @param url url to validate\n     * @return {@code true} if valid\n     */\n    static boolean isConnectionValid(final String url) {\n        boolean result = false;\n\n        try (final Connection ignored = DriverManager.getConnection(url)) {\n            logger.fine(\"Connection is valid\");\n            result = true;\n        } catch (final SQLException e) {\n            if (e.toString().contains(\"Unknown database\")) {\n                logger.severe(\"Unknown database\");\n            }\n        } catch (final Exception e) {\n            logger.log(Level.SEVERE, e.toString(), e);\n        }\n\n        return result;\n    }\n\n    /**\n     * Forces a database closed and waits for the lock file to disappear indicating the database server is closed.\n     *\n     * @param dataStoreType     DataStoreType to connect to\n     * @param fileName          path of the file to close\n     * @param lockFileExtension lock file extension\n     * @param password          password for the database\n     */\n    static void waitForLockFileRelease(final DataStoreType dataStoreType, final String fileName,\n                                       final String lockFileExtension, final char[] password) {\n\n        final String lockFile = FileUtils.stripFileExtension(fileName) + lockFileExtension;\n        logger.log(Level.INFO, \"Searching for lock file: {0}\", lockFile);\n\n        // Don't try if the lock file does not exist\n        if (Files.exists(Paths.get(lockFile))) {\n            final Properties properties = JpaConfiguration.getLocalProperties(dataStoreType, fileName, password, false);\n            final String url = properties.getProperty(JpaConfiguration.JAVAX_PERSISTENCE_JDBC_URL);\n\n            // Send shutdown to close the database\n            try (final Connection connection = DriverManager.getConnection(url, JpaConfiguration.DEFAULT_USER,\n                    new String(password))) {\n                // must issue a shutdown for correct file closure\n                try (final Statement statement = connection.createStatement()) {\n                    statement.execute(SHUTDOWN);\n                }\n            } catch (final SQLException e) {\n                logger.log(Level.SEVERE, e.getMessage(), e);\n            }\n\n            logger.info(\"SQL SHUTDOWN was issued\");\n\n            if (!FileUtils.waitForFileRemoval(lockFile, MAX_LOCK_RELEASE_TIME)) {\n                logger.warning(\"Exceeded the maximum wait time for the file lock release\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/ChannelEvent.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\n/**\n * Standard message channel events.\n * \n * @author Craig Cavanaugh\n */\npublic enum ChannelEvent {\n    ACCOUNT_ADD,\n    ACCOUNT_ADD_FAILED,\n    ACCOUNT_ATTRIBUTE_MODIFY,\n    ACCOUNT_MODIFY,\n    ACCOUNT_MODIFY_FAILED,\n    ACCOUNT_REMOVE,\n    ACCOUNT_REMOVE_FAILED,\n    ACCOUNT_SECURITY_ADD,\n    ACCOUNT_SECURITY_ADD_FAILED,\n    ACCOUNT_SECURITY_REMOVE,\n    ACCOUNT_SECURITY_REMOVE_FAILED,\n    ACCOUNT_VISIBILITY_CHANGE,\n    ACCOUNT_VISIBILITY_CHANGE_FAILED,\n    BACKGROUND_PROCESS_STARTED,\n    BACKGROUND_PROCESS_STOPPED,\n    BUDGET_ADD,\n    BUDGET_ADD_FAILED,\n    BUDGET_GOAL_UPDATE,\n    BUDGET_GOAL_UPDATE_FAILED,\n    BUDGET_UPDATE,\n    BUDGET_UPDATE_FAILED,\n    BUDGET_REMOVE,\n    CONFIG_MODIFY,\n    // CONFIG_MODIFY_FAILED,\n    CURRENCY_ADD,\n    CURRENCY_ADD_FAILED,\n    CURRENCY_MODIFY,\n    CURRENCY_MODIFY_FAILED,\n    CURRENCY_REMOVE,\n    CURRENCY_REMOVE_FAILED,\n    EXCHANGE_RATE_ADD,\n    EXCHANGE_RATE_ADD_FAILED,\n    EXCHANGE_RATE_REMOVE,\n    EXCHANGE_RATE_REMOVE_FAILED,\n    REMINDER_ADD,\n    REMINDER_ADD_FAILED,\n    REMINDER_REMOVE,\n    REMINDER_UPDATE,\n    REMINDER_UPDATE_FAILED,\n    SECURITY_ADD,\n    SECURITY_ADD_FAILED,\n    SECURITY_MODIFY,\n    SECURITY_MODIFY_FAILED,\n    SECURITY_REMOVE,\n    SECURITY_REMOVE_FAILED,\n    SECURITY_HISTORY_ADD,\n    SECURITY_HISTORY_ADD_FAILED,\n    SECURITY_HISTORY_REMOVE,\n    SECURITY_HISTORY_REMOVE_FAILED,\n    SECURITY_HISTORY_EVENT_ADD,\n    SECURITY_HISTORY_EVENT_ADD_FAILED,\n    SECURITY_HISTORY_EVENT_REMOVE,\n    SECURITY_HISTORY_EVENT_REMOVE_FAILED,\n    TRANSACTION_ADD,\n    TRANSACTION_ADD_FAILED,\n    TRANSACTION_REMOVE,\n    TRANSACTION_REMOVE_FAILED,\n    TAG_ADD,\n    TAG_ADD_FAILED,\n    TAG_MODIFY,\n    TAG_MODIFY_FAILED,\n    TAG_REMOVE,\n    TAG_REMOVE_FAILED,\n    FILE_CLOSING,\n    FILE_NOT_FOUND,\n    FILE_IO_ERROR,\n    FILE_LOAD_FAILED,\n    FILE_LOAD_SUCCESS,\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/LocalServerListener.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.engine.message;\n\n/**\n * Classes must implement this interface to register and listen to message events.\n *\n * @author Craig Cavanaugh\n */\npublic interface LocalServerListener {\n    void messagePosted(String event);\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/Message.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.StoredObject;\nimport jgnash.util.NotNull;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.util.EnumMap;\nimport java.util.Objects;\nimport java.util.UUID;\n\n/**\n * Message object.\n *\n * @author Craig Cavanaugh\n */\npublic class Message implements Serializable, Cloneable {\n\n    private ChannelEvent event;\n\n    private MessageChannel channel;\n\n    private String source;\n\n    private transient EnumMap<MessageProperty, StoredObject> properties = new EnumMap<>(MessageProperty.class);\n\n    /**\n     * Used to flag message sent remotely.\n     */\n    private transient boolean remote;\n\n    /**\n     * No argument constructor for reflection purposes.<br>\n     * <b>Do not use to create new instances</b>\n     *\n     * @deprecated\n     */\n    @Deprecated\n    public Message() {\n    }\n\n    public Message(final MessageChannel channel, final ChannelEvent event, final Engine source) {\n        this(channel, event, source.getUuid());\n    }\n\n    private Message(final MessageChannel channel, final ChannelEvent event, final String source) {\n        this.source = Objects.requireNonNull(source);\n        this.event = Objects.requireNonNull(event);\n        this.channel = Objects.requireNonNull(channel);\n    }\n\n    public MessageChannel getChannel() {\n        return channel;\n    }\n\n    public ChannelEvent getEvent() {\n        return event;\n    }\n\n    /**\n     * Sets a message property. The value must be reachable by the engine or and exception will be thrown.\n     *\n     * @param key   property key\n     * @param value message value\n     */\n    public void setObject(@NotNull final MessageProperty key, @NotNull final StoredObject value) {\n        properties.put(Objects.requireNonNull(key), Objects.requireNonNull(value));\n    }\n\n    /**\n     * Returns a {@code StoredObject} given a property key.\n     *\n     * @param key {@code MessageProperty} to search for\n     * @param <T> instance of {@code StoredObject}\n     * @return object if found, {@code null} otherwise\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T extends StoredObject> T getObject(final MessageProperty key) {\n        return (T) properties.get(key);\n    }\n\n    public String getSource() {\n        return source;\n    }\n\n    void setRemote() {\n        remote = true;\n    }\n\n    public boolean isRemote() {\n        return remote;\n    }\n\n    /**\n     * Write message out to ObjectOutputStream.\n     *\n     * @param s stream\n     * @throws IOException io exception\n     * @serialData Write serializable fields, if any exist. Write out the integer count of properties. Write out key and\n     * value of each property\n     */\n    @SuppressWarnings(\"unused\")\n    private void writeObject(final ObjectOutputStream s) throws IOException {\n        s.defaultWriteObject();\n\n        // write the property count\n        s.writeInt(properties.size());\n\n        StoredObject[] values = properties.values().toArray(new StoredObject[0]);\n        MessageProperty[] keys = properties.keySet().toArray(new MessageProperty[0]);\n\n        for (int i = 0; i < properties.size(); i++) {\n            s.writeObject(keys[i]);\n            s.writeUTF(values[i].getClass().getName());\n            s.writeUTF(values[i].getUuid().toString());\n        }\n    }\n\n    /**\n     * Read a Message from an ObjectInputStream.\n     *\n     * @param s input stream\n     * @throws java.io.IOException    io exception\n     * @throws ClassNotFoundException thrown is class is not found\n     * @serialData Read serializable fields, if any exist. Read the integer count of properties. Read the key and value\n     * of each property\n     */\n    @SuppressWarnings({\"unchecked\", \"unused\"})\n    private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {\n        s.defaultReadObject();\n\n        properties = new EnumMap<>(MessageProperty.class);\n\n        final int size = s.readInt();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (int i = 0; i < size; i++) {\n            MessageProperty key = (MessageProperty) s.readObject();\n            Class<? extends StoredObject> clazz = (Class<? extends StoredObject>) Class.forName(s.readUTF());\n            StoredObject value = engine.getStoredObjectByUuid(clazz, UUID.fromString(s.readUTF()));\n            properties.put(key, value);\n        }\n    }\n\n    @Override\n    public Message clone() throws CloneNotSupportedException {\n        final Message m = (Message) super.clone();\n        m.properties = properties.clone();\n\n        return m;\n    }\n\n    /**\n     * Returns the event and channel for this message.\n     *\n     * @return event and channel information\n     * @see java.lang.Object#toString()\n     */\n    @Override\n    public String toString() {\n        return String.format(\"Message [event=%s, channel=%s, source=%s]\", event, channel, source);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageBus.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\nimport java.lang.ref.WeakReference;\nimport java.time.LocalDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.util.DefaultDaemonThreadFactory;\n\n/**\n * Thread safe Message Bus.\n *\n * Listeners are stored in a CopyOnWriteArraySet to prevent duplicate listeners\n * and to ease the burden of synchronizing against multiple threads.  The iterator\n * must be used for access, but removal of weak references must be done through the\n * set, not the iterator.\n *\n * @author Craig Cavanaugh\n */\npublic class MessageBus {\n\n    /**\n     * Maximum wait to get a valid response from the remote message bus before giving up.\n     */\n    private static final long MAX_LATENCY = 5L * 1000L;\n\n    private static final Logger logger = Logger.getLogger(MessageBus.class.getName());\n\n    private final ConcurrentMap<MessageChannel, Set<WeakReference<MessageListener>>> map = new ConcurrentHashMap<>();\n\n    private final ExecutorService pool = Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory(\"Message Bus Executor\"));\n\n    private MessageBusClient messageBusClient = null;\n\n    private static final Map<String, MessageBus> busMap = new HashMap<>();\n\n    private static final String DEFAULT = \"default\";\n\n    /** Name given to this message bus instance. */\n    private final String busName;\n\n    private MessageBus(final String busName) {\n        this.busName = busName;\n    }\n\n    /**\n     * Set message bus to post remote messages.\n     *\n     * @param host message server name or IP address\n     * @param port message server port\n     * @param password connection password\n     * @return {@code true} if connection to the remote server was successful\n     */\n    public synchronized boolean setRemote(final String host, final int port, final char[] password) {\n        disconnectFromServer();\n        return connectToServer(host, port, password);\n    }\n\n    /**\n     * Set message bus for local operation.\n     */\n    public synchronized void setLocal() {\n        disconnectFromServer();\n    }\n\n    /**\n     * Returns the message bus instance intended for UI use only.\n     *\n     * @return an instance of a MessageBus\n     */\n    public static synchronized MessageBus getInstance() {\n        return getInstance(DEFAULT);\n    }\n\n    public static synchronized MessageBus getInstance(final String name) {\n        return busMap.computeIfAbsent(name, k -> new MessageBus(name));\n    }\n\n    public String getRemoteDataBasePath() {\n        if (messageBusClient != null) {\n            return messageBusClient.getDataBasePath();\n        }\n        return null;\n    }\n\n    public DataStoreType getRemoteDataStoreType() {\n        if (messageBusClient != null) {\n            return messageBusClient.getDataStoreType();\n        }\n        return null;\n    }\n\n    private void disconnectFromServer() {\n        if (messageBusClient != null) {\n            messageBusClient.disconnectFromServer();\n            messageBusClient = null;\n        }\n    }\n\n    /**\n     * Issues a shutdown request to a remote server.\n     *\n     * @param remoteHost message server name or IP address\n     * @param remotePort message server port\n     * @param password connection password   \n     */\n    public void shutDownRemoteServer(final String remoteHost, final int remotePort, final char[] password) {\n        if (remoteHost == null || remotePort <= 0) {\n            throw new IllegalArgumentException();\n        }\n\n        MessageBusClient client = new MessageBusClient(remoteHost, remotePort, busName);\n\n        if (client.connectToServer(password)) {\n            client.sendRemoteShutdownRequest();\n        }\n    }\n\n    private boolean connectToServer(final String remoteHost, final int remotePort, final char[] password) {\n        if (remoteHost == null || remotePort <= 0) {\n            throw new IllegalArgumentException();\n        }\n\n        messageBusClient = new MessageBusClient(remoteHost, remotePort, busName);\n\n        boolean result = messageBusClient.connectToServer(password);\n\n        if (result) {\n            final LocalDateTime start = LocalDateTime.now();\n\n            // wait for the server response to the remote database path for a max delay before timing out\n            // this is the handshake that a good connection was made\n            while (getRemoteDataBasePath() == null || getRemoteDataStoreType() == null) {\n                if (ChronoUnit.MILLIS.between(start, LocalDateTime.now()) > MAX_LATENCY) {\n                    disconnectFromServer();\n                    logger.warning(\"Did not receive a valid response from the server\");\n                    result = false;\n                    break;\n                }\n            }\n        } else {\n            messageBusClient = null; //make sure bad client connections are dumped\n        }\n\n        return result;\n    }\n\n    public void registerListener(final MessageListener listener, final MessageChannel... channels) {\n        for (final MessageChannel channel : channels) {\n            final Set<WeakReference<MessageListener>> set\n                    = map.computeIfAbsent(channel, k -> new CopyOnWriteArraySet<>());\n\n            if (containsListener(listener, channel)) {\n                logger.severe(\"An attempt was made to install a duplicate listener\");\n                logStackTrace();\n            } else {\n                set.add(new WeakReference<>(listener));\n            }\n        }\n    }\n\n    private static void logStackTrace() {\n        final StringBuilder trace = new StringBuilder(\"Stack Trace\" + System.lineSeparator());\n\n        for (final StackTraceElement element : Thread.currentThread().getStackTrace()) {\n            trace.append(\"\\tat \").append(element).append(System.lineSeparator());\n        }\n\n        logger.log(Level.SEVERE, trace.toString());\n    }\n\n    public void unregisterListener(final MessageListener listener, final MessageChannel... channels) {\n        for (MessageChannel channel : channels) {\n            Set<WeakReference<MessageListener>> set = map.get(channel);\n\n            if (set != null) {\n                for (WeakReference<MessageListener> ref : set) {\n                    MessageListener l = ref.get();\n                    if (l == null || l == listener) {\n                        set.remove(ref);\n                    }\n                }\n            }\n        }\n    }\n\n    private boolean containsListener(final MessageListener listener, final MessageChannel channel) {\n        Set<WeakReference<MessageListener>> set = map.get(channel);\n\n        if (set != null) {\n            for (WeakReference<MessageListener> ref : set) {\n                MessageListener l = ref.get();\n                if (l == listener) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Fires an event and blocks until all listeners have processed it.\n     *\n     * @param message {@code Message} to send\n     */\n    public void fireBlockingEvent(final Message message) {\n        final Future<Void> future = fireEvent(message);\n\n        // spin until everyone has consumed the event\n        while(!future.isDone()) {\n            Thread.onSpinWait();\n        }\n    }\n\n    /**\n     * Fires and event to all listeners and return immediately with a {@code Future}\n     * @param message {@code Message} to send\n     *\n     * @return {@code Future} indicating when all listeners have processed the event\n     */\n    public Future<Void> fireEvent(final Message message) {\n\n        return pool.submit(() -> {\n            final Set<WeakReference<MessageListener>> staleListener = new HashSet<>();\n\n            // Look for and post to local listeners\n            final Set<WeakReference<MessageListener>> set = map.get(message.getChannel());\n\n            if (set != null) {\n                for (final WeakReference<MessageListener> ref : set) {\n                    MessageListener l = ref.get();\n                    if (l != null) {\n                        l.messagePosted(message);\n                    } else {\n                        staleListener.add(ref);\n                    }\n                }\n            }\n\n            // purge stale references to prevent a slowdown and wasted memory during a long application session\n            for (final WeakReference<MessageListener> staleReference : staleListener) {\n                set.remove(staleReference);\n            }\n\n            /* Post a remote message if configured to do so and filter system events.\n             *\n             * Do not re-post a remote message otherwise it will just loop through the\n             * remote message system\n             * */\n            if (!message.isRemote() && messageBusClient != null && message.getChannel() != MessageChannel.SYSTEM) {\n                messageBusClient.sendRemoteMessage(message);\n            }\n\n            return null;\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageBusClient.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandlerAdapter;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.util.CharsetUtil;\nimport io.netty.util.ReferenceCountUtil;\n\nimport java.io.CharArrayWriter;\nimport java.util.Objects;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.Config;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.jpa.JpaNetworkServer;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.net.ConnectionFactory;\nimport jgnash.util.EncryptionManager;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.io.xml.CompactWriter;\nimport jgnash.util.LogUtil;\n\n/**\n * Message bus client for remote connections.\n *\n * @author Craig Cavanaugh\n */\nclass MessageBusClient {\n    private final String host;\n\n    private final int port;\n\n    private static final Logger logger = Logger.getLogger(MessageBusClient.class.getName());\n\n    private final XStream xstream;\n\n    private String dataBasePath;\n\n    private DataStoreType dataBaseType;\n\n    private EncryptionManager encryptionManager = null;\n\n    private NioEventLoopGroup eventLoopGroup;\n\n    private Channel channel;\n\n    private final String name;\n\n    private final ReentrantLock channelLock = new ReentrantLock();\n\n    static {\n        logger.setLevel(Level.INFO);\n    }\n\n    MessageBusClient(final String host, final int port, final String name) {\n        this.host = host;\n        this.port = port;\n        this.name = name;\n\n        xstream = XStreamFactory.getInstance();\n    }\n\n    String getDataBasePath() {\n        return dataBasePath;\n    }\n\n    DataStoreType getDataStoreType() {\n        return dataBaseType;\n    }\n\n    private static int getConnectionTimeout() {\n        return ConnectionFactory.getConnectionTimeout();\n    }\n\n    boolean connectToServer(final char[] password) {\n        boolean result = false;\n\n        // If a password has been specified, create an EncryptionManager\n        if (password != null && password.length > 0) {\n            encryptionManager = new EncryptionManager(password);\n        }\n\n        eventLoopGroup = new NioEventLoopGroup();\n\n        final Bootstrap bootstrap = new Bootstrap();\n\n        bootstrap.group(eventLoopGroup)\n                .channel(NioSocketChannel.class)\n                .handler(new MessageBusClientInitializer())\n                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectionTimeout() * 1000)\n                .option(ChannelOption.SO_KEEPALIVE, true);\n\n        channelLock.lock();\n\n        try {\n            // Start the connection attempt.\n            channel = bootstrap.connect(host, port).sync().channel();\n\n            result = true;\n            logger.info(\"Connected to remote message server\");\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, \"Failed to connect to remote message bus\", e);\n            disconnectFromServer();\n        } finally {\n            channelLock.unlock();\n        }\n\n        return result;\n    }\n\n    private class MessageBusClientInitializer extends ChannelInitializer<SocketChannel> {\n\n        @Override\n        public void initChannel(final SocketChannel ch) {\n            ChannelPipeline pipeline = ch.pipeline();\n\n            // Add the text line codec combination first,\n            pipeline.addLast(\"framer\", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));\n            pipeline.addLast(\"decoder\", new StringDecoder(CharsetUtil.UTF_8));\n            pipeline.addLast(\"encoder\", new StringEncoder(CharsetUtil.UTF_8));\n\n            // and then business logic.\n            pipeline.addLast(\"handler\", new MessageBusClientHandler());\n        }\n    }\n\n    /**\n     * Handles a client-side channel.\n     */\n    @ChannelHandler.Sharable\n    private class MessageBusClientHandler extends ChannelInboundHandlerAdapter {\n\n        private final ExecutorService executorService = Executors.newSingleThreadExecutor();\n\n        private String decrypt(final Object object) {\n            String plainMessage;\n\n            if (encryptionManager != null) {\n                plainMessage = encryptionManager.decrypt(object.toString());\n            } else {\n                plainMessage = object.toString();\n            }\n\n            return plainMessage;\n        }\n\n        @Override\n        public void channelRead(final ChannelHandlerContext ctx, final Object msg) {\n\n            try {\n                final String plainMessage = decrypt(msg);\n\n                logger.log(Level.FINE, \"messageReceived: {0}\", plainMessage);\n\n                if (plainMessage.startsWith(\"<Message\")) {\n                    executorService.submit(() -> {\n                        final Message message = (Message) xstream.fromXML(plainMessage);\n\n                        final Engine engine = EngineFactory.getEngine(name);\n                        Objects.requireNonNull(engine);\n\n                        // ignore our own messages\n                        if (!engine.getUuid().equals(message.getSource())) {\n                            processRemoteMessage(message);\n                        }\n                    });\n                } else if (plainMessage.startsWith(MessageBusServer.PATH_PREFIX)) {\n                    dataBasePath = plainMessage.substring(MessageBusServer.PATH_PREFIX.length());\n                    logger.log(Level.FINE, \"Remote data path is: {0}\", dataBasePath);\n                } else if (plainMessage.startsWith(MessageBusServer.DATA_STORE_TYPE_PREFIX)) {\n                    dataBaseType = DataStoreType.valueOf(plainMessage.substring(MessageBusServer.DATA_STORE_TYPE_PREFIX.length()));\n                    logger.log(Level.FINE, \"Remote dataBaseType type is: {0}\", dataBaseType.name());\n                } else if (plainMessage.startsWith(EncryptionManager.DECRYPTION_ERROR_TAG)) {    // decryption has failed, shut down the engine\n                    logger.log(Level.SEVERE, \"Unable to decrypt the remote message\");\n                } else if (plainMessage.startsWith(JpaNetworkServer.STOP_SERVER_MESSAGE)) {\n                    logger.info(\"Server is shutting down\");\n                    EngineFactory.closeEngine(name);\n                } else {\n                    logger.log(Level.SEVERE, \"Unknown message: {0}\", plainMessage);\n                }\n            } finally {\n                ReferenceCountUtil.release(msg);\n            }\n        }\n\n        @Override\n        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {\n            super.exceptionCaught(ctx, cause);\n            logger.log(Level.WARNING, \"Unexpected exception from downstream.\", cause);\n            ctx.close();\n        }\n    }\n\n    void disconnectFromServer() {\n\n        channelLock.lock();\n\n        try {\n            if (channel != null) {  // null from a prior failed connection\n                channel.close().sync();\n            }\n        } catch (final InterruptedException e) {\n            LogUtil.logSevere(MessageBusClient.class, e);\n        } finally {\n            channelLock.unlock();\n        }\n\n        eventLoopGroup.shutdownGracefully();\n\n        channel = null;\n        eventLoopGroup = null;\n    }\n\n    synchronized void sendRemoteMessage(final Message message) {\n        CharArrayWriter writer = new CharArrayWriter();\n        xstream.marshal(message, new CompactWriter(writer));\n\n        sendRemoteMessage(writer.toString());\n\n        logger.log(Level.FINE, \"sent: {0}\", writer);\n    }\n\n    void sendRemoteShutdownRequest() {\n        sendRemoteMessage(JpaNetworkServer.STOP_SERVER_MESSAGE);\n    }\n\n    private void sendRemoteMessage(final String message) {\n        channelLock.lock();\n\n        try {\n            if (encryptionManager != null) {\n                channel.writeAndFlush(encryptionManager.encrypt(message) + MessageBusServer.EOL_DELIMITER).sync();\n            } else {\n                channel.writeAndFlush(message + MessageBusServer.EOL_DELIMITER).sync();\n            }\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        } catch (final NullPointerException e) {\n            if (channel == null) {\n                logger.info(\"Channel was null\");\n            }\n\n            logger.log(Level.INFO, \"Tried to send message: {0} through a null channel\", message);\n        } finally {\n            channelLock.unlock();\n        }\n    }\n\n    /**\n     * Takes a remote message and forces remote updates before sending the message to the MessageBus to notify UI\n     * components of changes.\n     *\n     * @param message Message to process and send\n     */\n    private void processRemoteMessage(final Message message) {\n        logger.fine(\"processing a remote message\");\n\n        final Engine engine = EngineFactory.getEngine(name);\n        Objects.requireNonNull(engine);\n\n        if (message.getChannel() == MessageChannel.ACCOUNT) {\n            final Account account = message.getObject(MessageProperty.ACCOUNT);\n            switch (message.getEvent()) {\n                case ACCOUNT_ADD:\n                case ACCOUNT_REMOVE:\n                    engine.refresh(account);\n                    message.setObject(MessageProperty.ACCOUNT, engine.getAccountByUuid(account.getUuid()));\n                    engine.refresh(account.getParent());\n                    break;\n                case ACCOUNT_MODIFY:\n                case ACCOUNT_SECURITY_ADD:\n                case ACCOUNT_SECURITY_REMOVE:\n                case ACCOUNT_VISIBILITY_CHANGE:\n                    engine.refresh(account);\n                    message.setObject(MessageProperty.ACCOUNT, engine.getAccountByUuid(account.getUuid()));\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        if (message.getChannel() == MessageChannel.BUDGET) {\n            final Budget budget = message.getObject(MessageProperty.BUDGET);\n            switch (message.getEvent()) {\n                case BUDGET_ADD:\n                case BUDGET_UPDATE:\n                case BUDGET_REMOVE:\n                case BUDGET_GOAL_UPDATE:\n                    engine.refresh(budget);\n                    message.setObject(MessageProperty.BUDGET, engine.getBudgetByUuid(budget.getUuid()));\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        if (message.getChannel() == MessageChannel.COMMODITY) {\n            switch (message.getEvent()) {\n                case CURRENCY_ADD:\n                case CURRENCY_MODIFY:\n                    final CommodityNode currency = message.getObject(MessageProperty.COMMODITY);\n                    engine.refresh(currency);\n                    message.setObject(MessageProperty.COMMODITY, engine.getCurrencyNodeByUuid(currency.getUuid()));\n                    break;\n                case SECURITY_ADD:\n                case SECURITY_MODIFY:\n                case SECURITY_HISTORY_ADD:\n                case SECURITY_HISTORY_REMOVE:\n                    final CommodityNode node = message.getObject(MessageProperty.COMMODITY);\n                    engine.refresh(node);\n                    message.setObject(MessageProperty.COMMODITY, engine.getSecurityNodeByUuid(node.getUuid()));\n                    break;\n                case EXCHANGE_RATE_ADD:\n                case EXCHANGE_RATE_REMOVE:\n                    final ExchangeRate rate = message.getObject(MessageProperty.EXCHANGE_RATE);\n                    engine.refresh(rate);\n                    message.setObject(MessageProperty.EXCHANGE_RATE, engine.getExchangeRateByUuid(rate.getUuid()));\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        if (message.getChannel() == MessageChannel.CONFIG && message.getEvent() == ChannelEvent.CONFIG_MODIFY) {\n            final Config config = message.getObject(MessageProperty.CONFIG);\n            engine.refresh(config);\n            message.setObject(MessageProperty.CONFIG, engine.getStoredObjectByUuid(Config.class, config.getUuid()));\n        }\n\n        if (message.getChannel() == MessageChannel.REMINDER) {\n            switch (message.getEvent()) {\n                case REMINDER_ADD:\n                case REMINDER_REMOVE:\n                    final Reminder reminder = message.getObject(MessageProperty.REMINDER);\n                    engine.refresh(reminder);\n                    message.setObject(MessageProperty.REMINDER, engine.getReminderByUuid(reminder.getUuid()));\n                    break;\n                default:\n                    break;\n\n            }\n        }\n\n        if (message.getChannel() == MessageChannel.TRANSACTION) {\n            switch (message.getEvent()) {\n                case TRANSACTION_ADD:\n                case TRANSACTION_REMOVE:\n                    final Transaction transaction = message.getObject(MessageProperty.TRANSACTION);\n                    engine.refresh(transaction);\n                    message.setObject(MessageProperty.TRANSACTION, engine.getTransactionByUuid(transaction.getUuid()));\n\n                    final Account account = message.getObject(MessageProperty.ACCOUNT);\n                    engine.refresh(account);\n                    message.setObject(MessageProperty.ACCOUNT, engine.getAccountByUuid(account.getUuid()));\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        /* Flag the message as remote */\n        message.setRemote();\n\n        logger.fine(\"fire remote message\");\n        MessageBus.getInstance(name).fireEvent(message);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageBusServer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandlerAdapter;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.group.ChannelGroup;\nimport io.netty.channel.group.DefaultChannelGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport io.netty.handler.codec.DelimiterBasedFrameDecoder;\nimport io.netty.handler.codec.Delimiters;\nimport io.netty.handler.codec.string.StringDecoder;\nimport io.netty.handler.codec.string.StringEncoder;\nimport io.netty.util.CharsetUtil;\nimport io.netty.util.ReferenceCountUtil;\nimport io.netty.util.concurrent.GlobalEventExecutor;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.util.EncryptionManager;\n\n/**\n * Message bus server for remote connections.\n *\n * @author Craig Cavanaugh\n */\npublic class MessageBusServer {\n\n    private static final Logger logger = Logger.getLogger(MessageBusServer.class.getName());\n\n    static final String PATH_PREFIX = \"<PATH>\";\n\n    static final String DATA_STORE_TYPE_PREFIX = \"<TYPE>\";\n\n    static final String EOL_DELIMITER = \"\\r\\n\";\n\n    private final int port;\n\n    private String dataBasePath = \"\";\n\n    private String dataStoreType = \"\";\n\n    private NioEventLoopGroup eventLoopGroup;\n\n    private final ReadWriteLock rwl = new ReentrantReadWriteLock(true);\n\n    private final Set<LocalServerListener> listeners = new HashSet<>();\n\n    private final ChannelGroup channelGroup = new DefaultChannelGroup(\"all-connected\", GlobalEventExecutor.INSTANCE);\n\n    private EncryptionManager encryptionManager;\n\n    private final ExecutorService executorService = Executors.newSingleThreadExecutor();\n\n    static {\n        logger.setLevel(Level.INFO);\n    }\n\n    public MessageBusServer(final int port) {\n        this.port = port;\n    }\n\n    public boolean startServer(final DataStoreType dataStoreType, final String dataBasePath, final char[] password) {\n        boolean result = false;\n\n        logger.info(\"Starting message bus server on port: \" + port);\n\n        this.dataBasePath = dataBasePath;\n        this.dataStoreType = dataStoreType.name();\n\n        // If a password has been specified, create an EncryptionManager\n        if (password != null && password.length > 0) {\n            encryptionManager = new EncryptionManager(password);\n        }\n\n        eventLoopGroup = new NioEventLoopGroup();\n\n        final ServerBootstrap bootstrap = new ServerBootstrap();\n\n        try {\n            bootstrap.group(eventLoopGroup)\n                    .channel(NioServerSocketChannel.class)\n                    .childHandler(new MessageBusRemoteInitializer())\n                    .childOption(ChannelOption.SO_KEEPALIVE, true);\n\n            final ChannelFuture future = bootstrap.bind(port);\n            future.sync();\n\n            if (future.isDone() && future.isSuccess()) {\n                logger.info(\"Message Bus Server started successfully\");\n                result = true;\n            } else {\n                logger.info(\"Failed to start the Message Bus Server\");\n            }\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            stopServer();\n        }\n\n        return result;\n    }\n\n    public void stopServer() {\n        rwl.writeLock().lock();\n\n        try {\n            channelGroup.close().sync();\n\n            executorService.shutdown();\n            eventLoopGroup.shutdownGracefully();\n\n            eventLoopGroup = null;\n\n            listeners.clear();\n\n            logger.info(\"MessageBusServer Stopped\");\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        } finally {\n            rwl.writeLock().unlock();\n        }\n    }\n\n    public void addLocalListener(final LocalServerListener listener) {\n        rwl.writeLock().lock();\n\n        try {\n            listeners.add(listener);\n        } finally {\n            rwl.writeLock().unlock();\n        }\n    }\n\n    public void removeLocalListener(final LocalServerListener listener) {\n        rwl.writeLock().lock();\n\n        try {\n            listeners.remove(listener);\n        } finally {\n            rwl.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Utility method to encrypt a message.\n     *\n     * @param message message to encrypt\n     * @return encrypted message\n     */\n    private String encrypt(final String message) {\n        if (encryptionManager != null) {\n            return encryptionManager.encrypt(message);\n        }\n        return message;\n    }\n\n    private String decrypt(final String message) {\n        String plainMessage;\n\n        if (encryptionManager != null) {\n            plainMessage = encryptionManager.decrypt(message);\n        } else {\n            plainMessage = message;\n        }\n\n        return plainMessage;\n    }\n\n    private class MessageBusRemoteInitializer extends ChannelInitializer<SocketChannel> {\n\n        @Override\n        public void initChannel(final SocketChannel ch) {\n            ChannelPipeline pipeline = ch.pipeline();\n\n            // Add the text line codec combination first,\n            pipeline.addLast(\"framer\", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));\n\n            // the encoder and decoder are static as these are sharable\n            pipeline.addLast(\"decoder\", new StringDecoder(CharsetUtil.UTF_8));\n            pipeline.addLast(\"encoder\", new StringEncoder(CharsetUtil.UTF_8));\n\n            // and then business logic.\n            pipeline.addLast(\"handler\", new MessageBusServerHandler());\n        }\n    }\n\n    @ChannelHandler.Sharable\n    private class MessageBusServerHandler extends ChannelInboundHandlerAdapter {\n\n        @Override\n        public void channelActive(final ChannelHandlerContext ctx) {\n            channelGroup.add(ctx.channel()); // maintain channels\n\n            logger.log(Level.INFO, \"Remote connection from: {0}\", ctx.channel().remoteAddress().toString());\n\n            // Inform the client what they are talking with so they can establish a correct database url\n            ctx.writeAndFlush(encrypt(PATH_PREFIX + dataBasePath) + EOL_DELIMITER);\n            ctx.writeAndFlush(encrypt(DATA_STORE_TYPE_PREFIX + dataStoreType) + EOL_DELIMITER);\n        }\n\n        @Override\n        public void channelInactive(final ChannelHandlerContext ctx) throws Exception {\n            channelGroup.remove(ctx.channel());\n            super.channelInactive(ctx);\n        }\n\n        @Override\n        public void channelRead(final ChannelHandlerContext ctx, final Object msg) {\n            executorService.submit(() -> {\n                processMessage(msg.toString());\n\n                ReferenceCountUtil.release(msg);\n            });\n        }\n\n        private void processMessage(final String message) {\n            final String plainMessage = decrypt(message);\n\n            rwl.readLock().lock();\n\n            try {\n                channelGroup.writeAndFlush(encrypt(plainMessage) + EOL_DELIMITER).sync();\n\n                // Local listeners do not receive encrypted messages\n                for (LocalServerListener listener : listeners) {\n                    listener.messagePosted(plainMessage);\n                }\n\n                logger.log(Level.FINE, \"Broadcast: {0}\", plainMessage);\n            } catch (InterruptedException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            } finally {\n                rwl.readLock().unlock();\n            }\n        }\n\n        @Override\n        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {\n            logger.log(Level.WARNING, \"Unexpected exception from downstream.\", cause);\n            ctx.close();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageChannel.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\n/**\n * Default message channels used by the engine.\n *\n * @author Craig Cavanaugh\n */\npublic enum MessageChannel {\n    ACCOUNT, BUDGET, COMMODITY, CONFIG, REMINDER, SYSTEM, TAG, TRANSACTION\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageListener.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\n/**\n * Classes must implement this interface to register and lister the message events.\n *\n * @author Craig Cavanaugh\n */\npublic interface MessageListener {\n    void messagePosted(Message message);\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageProperty.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\n/**\n * Message property Enums.\n *\n * @author Craig Cavanaugh\n */\npublic enum MessageProperty {\n    ACCOUNT,\n    BUDGET,\n    COMMODITY,\n    CONFIG,\n    EXCHANGE_RATE,\n    REMINDER,\n    TAG,\n    TRANSACTION\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/MessageProxy.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * A Utility class that forwards messages to registered listeners.\n * <p>\n * This is useful for classes that must process a message first and then forward to other listeners for secondary\n * processing.\n *\n * @author Craig Cavanaugh\n */\npublic class MessageProxy {\n\n    /**\n     * Message Listeners.\n     */\n    private final List<WeakReference<MessageListener>> messageListeners = new ArrayList<>();\n\n    /**\n     * Concurrency lock.\n     */\n    private final ReentrantLock lock = new ReentrantLock();\n\n    /**\n     * Shared thread pool for all instances of MessageProxy.\n     */\n    private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();\n\n    /**\n     * Register a listener.\n     *\n     * @param messageListener listener to register with this proxy\n     */\n    public final void addMessageListener(final MessageListener messageListener) {\n\n        lock.lock();\n\n        try {\n            messageListeners.add(new WeakReference<>(messageListener));\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    /**\n     * Remove a listener.\n     *\n     * @param messageListener listener to register with this proxy\n     */\n    public final void removeMessageListener(final MessageListener messageListener) {\n        lock.lock();\n\n        try {\n            Iterator<WeakReference<MessageListener>> iterator = messageListeners.iterator();\n\n            while (iterator.hasNext()) {\n                WeakReference<MessageListener> reference = iterator.next();\n\n                MessageListener actionListener = reference.get();\n\n                if (actionListener == null || actionListener == messageListener) {\n                    iterator.remove();\n                }\n            }\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    /**\n     * Forwards a message to an listeners.\n     *\n     * @param message message to forward\n     */\n    public final void forwardMessage(final Message message) {\n\n        lock.lock();\n\n        try {\n\n            THREAD_POOL.submit(() -> {\n                Iterator<WeakReference<MessageListener>> iterator = messageListeners.iterator();\n\n                while (iterator.hasNext()) {\n                    WeakReference<MessageListener> reference = iterator.next();\n\n                    final MessageListener actionListener = reference.get();\n\n                    if (actionListener != null) {\n                        THREAD_POOL.submit(() -> actionListener.messagePosted(message));\n                    } else {\n                        iterator.remove();\n                    }\n                }\n            });\n        } finally {\n            lock.unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/message/XStreamFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.message;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;\nimport com.thoughtworks.xstream.io.xml.StaxDriver;\n\nimport jgnash.engine.xstream.XStreamJVM9;\n\n/**\n * Utility class to generate an XStream instance.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamFactory {\n    private XStreamFactory() {}\n\n    static XStream getInstance() {\n    \t\n        final XStream xstream = new XStreamJVM9(new PureJavaReflectionProvider(), new StaxDriver());\n        xstream.alias(\"Message\", Message.class);\n        xstream.alias(\"MessageProperty\", MessageProperty.class);\n\n        return xstream;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/DailyReminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.LocalDate;\n\nimport javax.persistence.Entity;\n\nimport jgnash.time.DateUtils;\n\n/**\n * A daily reminder.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class DailyReminder extends Reminder {\n\n    public DailyReminder() {\n    }\n\n    @Override\n    public RecurringIterator getIterator() {\n        return new DailyIterator();\n    }\n\n    @Override\n    public ReminderType getReminderType() {\n        return ReminderType.DAILY;\n    }\n\n    private class DailyIterator implements RecurringIterator {\n        private LocalDate base;\n\n        DailyIterator() {\n            if (getLastDate() != null) {\n                base = getLastDate();\n            } else {\n                base = getStartDate().minusDays(getIncrement());\n            }\n        }\n\n        @Override\n        public LocalDate next() {\n            if (isEnabled()) {\n                base = base.plusDays(getIncrement());\n\n                if (getEndDate() == null || DateUtils.before(base, getEndDate())) {\n                    return base;\n                }\n            }\n            return null;\n        }\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/MonthlyReminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.temporal.TemporalField;\nimport java.time.temporal.WeekFields;\nimport java.util.Locale;\n\nimport javax.persistence.Entity;\n\nimport jgnash.time.DateUtils;\n\n/**\n * A monthly reminder / iterator. Dates get a little weird when iterating by DAY\n * and the day is early or late in the month. Months may be skipped or multiple\n * days in the same month. Iterating by DATE will be much more consistent.\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic class MonthlyReminder extends Reminder {\n\n    /**\n     * Defines if increment is by day of the week or day of the month.\n     */\n    private int type = DATE;\n\n    static final int DATE = 0;\n\n    static final int DAY = 1;\n\n    public MonthlyReminder() {\n    }\n\n    @Override\n    public RecurringIterator getIterator() {\n        return new MonthlyIterator();\n    }\n\n    /**\n     * Returns the Monthly Reminder type.\n     *\n     * @return Returns the type.\n     */\n    public int getType() {\n        return type;\n    }\n\n    @Override\n    public ReminderType getReminderType() {\n        return ReminderType.MONTHLY;\n    }\n\n    /**\n     * Sets the Monthly Reminder type.\n     *\n     * @param type The type to set.\n     */\n    public void setType(int type) {\n        if (type == DATE || type == DAY) {\n            this.type = type;\n        }\n    }\n\n    private class MonthlyIterator implements RecurringIterator {\n        private LocalDate base;\n\n        MonthlyIterator() {\n            if (getLastDate() != null) {\n\n                base = getLastDate();\n\n                final TemporalField weekOfMonth = WeekFields.of(Locale.getDefault()).weekOfMonth();\n\n                final int week = base.get(weekOfMonth);     // extract the current week\n                final DayOfWeek day = base.getDayOfWeek();  // extract the current day of the week\n\n                base = base.with(weekOfMonth, week);        // force the week of the month\n                base = base.with(day);                      // force the day of the week\n            } else {\n                if (type == DATE) {\n                    base = getStartDate().minusMonths(getIncrement());\n                } else if (type == DAY) {\n                    base = getStartDate();\n\n                    final TemporalField weekOfMonth = WeekFields.of(Locale.getDefault()).weekOfMonth();\n\n                    final int week = base.get(weekOfMonth);             // extract the current week\n                    final DayOfWeek day = base.getDayOfWeek();          // extract the current day of the week\n\n                    base = getStartDate().minusMonths(getIncrement());  // decrement the month\n                    base = base.with(weekOfMonth, week);                // force the week of the month\n                    base = base.with(day);                              // force the day of the week\n                }\n            }\n        }\n\n        @Override\n        public LocalDate next() {\n            if (isEnabled()) {\n                if (type == DATE) {\n                    base = base.plusMonths(getIncrement());\n                } else {\n                    final TemporalField weekOfMonth = WeekFields.of(Locale.getDefault()).weekOfMonth();\n\n                    final int week = base.get(weekOfMonth);     // extract the current week\n                    final DayOfWeek day = base.getDayOfWeek();  // extract the current day of the week\n\n                    base = base.plusMonths(getIncrement());     // increment the month\n                    base = base.with(weekOfMonth, week);        // force the week of the month\n                    base = base.with(day);                      // force the day of the week\n\n                    // if plusMonths resulted in an invalid date and adjusted to the prior week, bump it a week\n                    if (base.get(weekOfMonth) > week) {\n                        base = base.plusWeeks(1);\n                        base = base.with(weekOfMonth, week);        // force the week of the month\n                        base = base.with(day);                      // force the day of the week\n                    }\n                }\n\n                if (getEndDate() == null || DateUtils.before(base, getEndDate())) {\n                    return base;\n                }\n            }\n            return null;\n        }\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/OneTimeReminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.LocalDate;\n\nimport javax.persistence.Entity;\n\n/**\n * A one time only reminder.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class OneTimeReminder extends Reminder {\n\n    public OneTimeReminder() {\n    }\n\n    @Override\n    public RecurringIterator getIterator() {\n        return new OneTimeIterator();\n    }\n\n    @Override\n    public ReminderType getReminderType() {\n        return ReminderType.ONETIME;\n    }\n\n    private class OneTimeIterator implements RecurringIterator {\n\n        private boolean end = false; // one time only trigger\n\n        @Override\n        public LocalDate next() {\n            if (isEnabled()) {\n                if (getLastDate() == null && !end) {\n                    end = true;\n                    return getStartDate();\n                }\n            }\n            return null;\n        }\n    }\n\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/PendingReminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.LocalDate;\n\nimport jgnash.util.NotNull;\n\n/**\n * Used to wrap a reminder and it's event date to aid sorting and display.\n * \n * @author Craig Cavanaugh\n */\npublic class PendingReminder implements Comparable<PendingReminder> {\n\n    private final Reminder reminder;\n\n    /**\n     * The date for the register if a transaction is generated.\n     */\n    final private LocalDate commitDate;\n\n    /**\n     * Approved state of the reminder.\n     */\n    private boolean approved;\n\n    public PendingReminder(final @NotNull Reminder reminder, final @NotNull LocalDate date) {\n        this.reminder = reminder;\n        commitDate = date;\n    }\n\n    @Override\n    public int compareTo(final @NotNull PendingReminder o) {\n        if (o.reminder == reminder && o.commitDate.equals(commitDate)) {\n            return 0;\n        }\n\n        if (o.reminder == reminder) {\n            return commitDate.compareTo(o.commitDate);\n        }\n\n        return reminder.compareTo(o.reminder);\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) {\n            return true;\n        }\n        \n        return o instanceof PendingReminder && ((PendingReminder) o).reminder == reminder && ((PendingReminder) o).commitDate.equals(commitDate);\n    }\n\n    @Override\n    public int hashCode() {\n        int hash = 5;\n        hash = 79 * hash + (reminder != null ? reminder.hashCode() : 0);\n        return 79 * hash + (commitDate != null ? commitDate.hashCode() : 0);\n    }\n\n    /**\n     * Returns true if approved for execution of the {@code Reminder}.\n     *\n     * @return Returns the approved.\n     */\n    public synchronized final boolean isApproved() {\n        return approved;\n    }\n\n    /**\n     * Controls approval state of the {@code Reminder}.\n     *\n     * @param approved The approved to set.\n     */\n    public synchronized final void setApproved(final boolean approved) {\n        this.approved = approved;\n    }\n\n    /**\n     * Gets the commit date for the associated {@code Reminder}.\n     *\n     * @return Returns the commitDate.\n     */\n    public synchronized final LocalDate getCommitDate() {\n        return commitDate;\n    }\n\n    /**\n     * Gets the {@code Reminder}.\n     *\n     * @return Returns the reminder.\n     */\n    public synchronized final Reminder getReminder() {\n        return reminder;\n    }\n\n    @Override\n    public String toString() {\n        return reminder.getDescription() + \" \" + commitDate;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/RecurringIterator.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.LocalDate;\n\n/**\n * Interface for recurring iterators.\n *\n * @author Craig Cavanaugh\n */\npublic interface RecurringIterator {\n\n    /**\n     * Returns the next date the event should occur.\n     *\n     * @return The next date in the sequence or null if the sequence is no longer valid.\n     */\n    LocalDate next();\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/Reminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.LocalDate;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.persistence.CascadeType;\nimport javax.persistence.Entity;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.OneToOne;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.Transaction;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * This is an abstract class for scheduled reminders.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic abstract class Reminder extends StoredObject implements Comparable<Reminder> {\n\n    /**\n     * Number of days to notify in advance.\n     */\n    private int daysAdvance;\n\n    /**\n     * Create the transaction automatically.\n     */\n    private boolean autoCreate;\n\n    /**\n     * Description for this reminder.\n     */\n    private String description;\n\n    /**\n     * Enabled state.\n     */\n    private boolean enabled = true;\n\n    /**\n     * The last date the reminder will stop executing, may be null (Bug #2860259).\n     */\n    private LocalDate endDate = null;\n\n    /**\n     * Number of periods to increment between events.\n     */\n    private int increment = 1;\n\n    /**\n     * The last date the reminder was executed.\n     * <p>\n     * It should remain an increment of the iterator for correct operation.\n     */\n    private LocalDate lastDate = null;\n\n    /**\n     * Notes for this reminder.\n     */\n    private String notes = null;\n\n    /**\n     * The start date of this reminder.\n     */\n    private LocalDate startDate = LocalDate.now();\n\n    @ManyToOne\n    private Account account;\n\n    /**\n     * Reference to the transaction for this reminder.\n     */\n    @OneToOne(orphanRemoval = true, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})\n    private Transaction transaction;\n\n    @Override\n    public int compareTo(@NotNull final Reminder reminder) {\n\n        int result = description.compareTo(reminder.description);\n\n        if (result != 0) {\n            return result;\n        }\n\n        result = getReminderType().compareTo(reminder.getReminderType());\n\n        if (result != 0) {\n            return result;\n        }\n\n        return getUuid().compareTo(reminder.getUuid());\n    }\n\n    /**\n     * Gets the number of days in advance the reminder should execute.\n     *\n     * @return Returns the advanceRemindDays.\n     */\n    public int getDaysAdvance() {\n        return daysAdvance;\n    }\n\n    /**\n     * Gets the description for the reminder.\n     *\n     * @return Returns the description.\n     */\n    public String getDescription() {\n        return description;\n    }\n\n    /**\n     * Gets the ending date for the reminder.\n     *\n     * @return Returns the last date the reminder should execute.\n     */\n    public @Nullable LocalDate getEndDate() {\n        return endDate;\n    }\n\n    /**\n     * Gets the {@code Iterator} for this {@code Reminder}.\n     *\n     * @return Returns the iterator.\n     */\n    public abstract RecurringIterator getIterator();\n\n    /**\n     * Gets the last recorded date for this {@code Reminder}.\n     * @return Returns the last recorded date\n     */\n    public @Nullable LocalDate getLastDate() {\n        return lastDate;\n    }\n\n    /**\n     * Gets the start date for this {@code Reminder}.\n     *\n     * @return Returns the start date.\n     */\n    public @NotNull LocalDate getStartDate() {\n        return startDate;\n    }\n\n    /**\n     * Returns a clone of the transaction.  A clone is returned to prevent\n     * accidental insertion of the original into the engine.\n     *\n     * @return Returns the transaction.\n     */\n    public Transaction getTransaction() {\n        if (transaction != null) {\n            try {\n                return (Transaction) transaction.clone();\n            } catch (CloneNotSupportedException ex) {\n                Logger.getLogger(Reminder.class.getName()).log(Level.SEVERE, null, ex);\n            }\n        }\n        return null;\n    }\n\n    public abstract ReminderType getReminderType();\n\n    @Override\n    public boolean equals(final Object other) {\n        return this == other ||\n                other != null && getClass() == other.getClass() && this.getUuid().equals(((Reminder) other).getUuid());\n\n    }\n\n    /**\n     * Returns true is this {@code Reminder} is executed without user interaction.\n     *\n     * @return Returns the autoCreate.\n     */\n    public boolean isAutoCreate() {\n        return autoCreate;\n    }\n\n    /**\n     * Getter for property enabled.\n     *\n     * @return Value of property enabled.\n     */\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n\n    /**\n     * Sets the number of days in advance this {@code Reminder} should execute.\n     *\n     * @param advanceRemindDays The advanceRemindDays to set.\n     */\n    public void setDaysAdvance(final int advanceRemindDays) {\n        this.daysAdvance = advanceRemindDays;\n    }\n\n    /**\n     * Controls auto entry of the {@code Reminder}.\n     * @param autoCreate The autoCreate to set.\n     */\n    public void setAutoCreate(final boolean autoCreate) {\n        this.autoCreate = autoCreate;\n    }\n\n    /**\n     * Sets the description for this {@code Reminder}.\n     *\n     * @param description The description to set.\n     */\n    public void setDescription(@NotNull final String description) {\n        Objects.requireNonNull(description);\n        this.description = description;\n    }\n\n    /**\n     * Setter for property enabled.\n     *\n     * @param enabled New value of property enabled.\n     */\n    public void setEnabled(final boolean enabled) {\n        this.enabled = enabled;\n    }\n\n    /**\n     * Sets the last date for the iterator in the series.\n     *\n     * @param endDate The last date the reminder should execute.\n     */\n    public void setEndDate(final @Nullable LocalDate endDate) {\n        this.endDate = endDate;\n    }\n\n    /**\n     * Sets the last date fired to the next iterator date in the series.\n     *\n     * @param lastDate The lastDate to set.\n     */\n    private void setLastDate(final @NotNull LocalDate lastDate) {\n        this.lastDate = lastDate;\n    }\n\n    /**\n     * Increment the last date fired to the next iterator date in the series.\n     */\n    public void setLastDate() {\n        setLastDate(getIterator().next());\n    }\n\n    /**\n     * Set the starting date for the reminder.\n     *\n     * @param startDate The startDate to set.\n     */\n    public void setStartDate(final @NotNull LocalDate startDate) {\n        this.startDate = startDate;\n    }\n\n    /**\n     * Assign the transaction to be entered upon acceptable of the reminder.\n     *\n     * @param transaction The transaction to set.\n     */\n    public void setTransaction(final Transaction transaction) {\n        this.transaction = transaction;\n    }\n\n    /**\n     * Get the Reminders's increment.\n     *\n     * @return Returns the increment.\n     */\n    public int getIncrement() {\n        return increment;\n    }\n\n    /**\n     * Set the Reminders's increment.\n     *\n     * @param increment The increment to set.\n     */\n    public void setIncrement(final int increment) {\n        this.increment = increment;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n    /**\n     * Sets the Reminder's notes.\n     *\n     * @param notes The notes to set.\n     */\n    public void setNotes(final String notes) {\n        this.notes = notes;\n    }\n\n    /**\n     * Get the Reminder's notes.\n     *\n     * @return Returns the notes.\n     */\n    public String getNotes() {\n        return notes;\n    }\n\n    /**\n     * Sets the base Account associated with the Reminder.\n     *\n     * @param account base account for this Reminder\n     */\n    public void setAccount(final Account account) {\n        this.account = account;\n    }\n\n    /**\n     * Returns the base Account associated with the Reminder.\n     *\n     * @return Returns the account\n     */\n    public Account getAccount() {\n        return account;\n    }\n\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n        Reminder r = (Reminder) super.clone();\n\n        // create a deep clone\n        r.setEndDate(getEndDate());\n        r.setStartDate(getStartDate());\n\n        if (getLastDate() != null) {\n            r.setLastDate(getLastDate());\n        }\n\n        if (getTransaction() != null) {\n            r.setTransaction((Transaction) getTransaction().clone());\n        }\n\n        return r;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/ReminderType.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Reminder type class.\n *\n * @author Craig Cavanaugh\n *\n */\npublic enum ReminderType {\n\n    ONETIME(ResourceUtils.getString(\"Period.OnlyOnce\")),\n    DAILY(ResourceUtils.getString(\"Period.Daily\")),\n    WEEKLY(ResourceUtils.getString(\"Period.Weekly\")),\n    MONTHLY(ResourceUtils.getString(\"Period.Monthly\")),\n    YEARLY(ResourceUtils.getString(\"Period.Yearly\"));\n\n    private final transient String typeName;\n\n    ReminderType(String name) {\n        typeName = name;\n    }\n\n    /**\n     * Prints the ReminderType name in the toString method.\n     */\n    @Override\n    public final String toString() {\n        return typeName;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/WeeklyReminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\n\nimport javax.persistence.Entity;\n\nimport jgnash.time.DateUtils;\n\n/**\n * A weekly reminder.\n *\n * @author Craig Cavanaugh\n */\n@Entity\npublic class WeeklyReminder extends Reminder {\n\n    public WeeklyReminder() {\n\n    }\n\n    @Override\n    public ReminderType getReminderType() {\n        return ReminderType.WEEKLY;\n    }\n\n    @Override\n    public RecurringIterator getIterator() {\n        return new WeeklyIterator();\n    }\n\n    private class WeeklyIterator implements RecurringIterator {\n        private LocalDate base;\n\n        WeeklyIterator() {\n            if (getLastDate() != null) {\n                final DayOfWeek dayOfWeek = DayOfWeek.from(getStartDate());\n\n                base = getLastDate().plusDays(1);   // plus one day to force next iteration\n\n                // adjust for actual target date, it could have been modified since the last date\n                base = (LocalDate) dayOfWeek.adjustInto(base);\n            } else {\n                base = getStartDate().minusWeeks(getIncrement());\n            }\n        }\n\n        @Override\n        public LocalDate next() {\n            if (isEnabled()) {\n                final DayOfWeek dayOfWeek = DayOfWeek.from(getStartDate());\n\n                base = base.plusWeeks(getIncrement());\n                base = (LocalDate) dayOfWeek.adjustInto(base);\n\n                if (getEndDate() == null || DateUtils.before(base, getEndDate())) {\n                    return base;\n                }\n            }\n            return null;\n        }\n    }\n\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/recurring/YearlyReminder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport java.time.LocalDate;\n\nimport javax.persistence.Entity;\n\nimport jgnash.time.DateUtils;\n\n/**\n * A yearly reminder.\n * \n * @author Craig Cavanaugh\n */\n@Entity\npublic class YearlyReminder extends Reminder {\n\n    public YearlyReminder() {\n\n    }\n\n    @Override\n    public RecurringIterator getIterator() {\n        return new YearlyIterator();\n    }\n\n    @Override\n    public ReminderType getReminderType() {\n        return ReminderType.YEARLY;\n    }\n\n    private class YearlyIterator implements RecurringIterator {\n        private LocalDate base;\n\n        YearlyIterator() {\n            if (getLastDate() != null) {\n                base = getLastDate();\n\n                if (getStartDate().lengthOfYear() == getStartDate().getDayOfYear()) {\n                    base = base.withDayOfYear(base.lengthOfYear());\n                } else {\n                    // adjust for actual target date, it could have been modified since the last date\n                    base = base.withDayOfYear(getStartDate().getDayOfYear());\n                }\n            } else {\n                base = getStartDate().minusYears(getIncrement());\n            }\n        }\n\n        @Override\n        public LocalDate next() {\n            if (isEnabled()) {\n                base = base.plusYears(getIncrement());\n\n                if (getStartDate().lengthOfYear() == getStartDate().getDayOfYear()) {\n                    base = base.withDayOfYear(base.lengthOfYear());\n                } else {\n                    // adjust for actual target date, it could have been modified since the last date\n                    base = base.withDayOfYear(getStartDate().getDayOfYear());\n                }\n\n                if (getEndDate() == null || DateUtils.before(base, getEndDate())) {\n                    return base;\n                }\n            }\n            return null;\n        }\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/AbstractXStreamContainer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AmortizeObject;\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.Config;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.ExchangeRateHistoryNode;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.Tag;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionEntryAddX;\nimport jgnash.engine.TransactionEntryBuyX;\nimport jgnash.engine.TransactionEntryDividendX;\nimport jgnash.engine.TransactionEntryMergeX;\nimport jgnash.engine.TransactionEntryReinvestDivX;\nimport jgnash.engine.TransactionEntryRemoveX;\nimport jgnash.engine.TransactionEntrySellX;\nimport jgnash.engine.TransactionEntrySplitX;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.budget.BudgetGoal;\nimport jgnash.time.Period;\nimport jgnash.util.FileLocker;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.converters.reflection.ReflectionProvider;\nimport com.thoughtworks.xstream.hibernate.converter.HibernatePersistentCollectionConverter;\nimport com.thoughtworks.xstream.hibernate.converter.HibernatePersistentMapConverter;\nimport com.thoughtworks.xstream.hibernate.converter.HibernateProxyConverter;\nimport com.thoughtworks.xstream.hibernate.mapper.HibernateMapper;\nimport com.thoughtworks.xstream.io.HierarchicalStreamDriver;\nimport com.thoughtworks.xstream.mapper.MapperWrapper;\nimport com.thoughtworks.xstream.security.ArrayTypePermission;\nimport com.thoughtworks.xstream.security.NoTypePermission;\nimport com.thoughtworks.xstream.security.PrimitiveTypePermission;\nimport com.thoughtworks.xstream.security.WildcardTypePermission;\n\n/**\n * Abstract XStream container.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractXStreamContainer {\n\t\n    private static final String DESCRIPTION = \"description\";\n    \n\tfinal List<StoredObject> objects = new ArrayList<>();\n    final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);\n    final Path path;\n\n    private final FileLocker fileLocker = new FileLocker();\n\n    AbstractXStreamContainer(final Path path) {\n        this.path = path;\n    }\n\n    /**\n     * Creates a backup file if it exists.\n     *\n     * @param origFile file to check and backup\n     */\n    static void createBackup(final Path origFile) {\n        if (Files.exists(origFile)) {\n\n            final Path backup = Paths.get(origFile + \".backup\");\n\n            if (Files.exists(backup)) {\n                try {\n                    Files.delete(backup);\n                } catch (IOException e) {\n                    Logger.getLogger(AbstractXStreamContainer.class.getName())\n                            .log(Level.WARNING, \"Was not able to delete the old backup file: {0}\", backup);\n                }\n            }\n\n            FileUtils.copyFile(origFile, backup);\n        }\n    }\n\n    /**\n     * Returns a list of objects that are assignable from from the specified Class.\n     * <p>\n     * The returned list may be modified without causing side effects\n     *\n     * @param <T>    the type of class to query\n     * @param values Collection of objects to query\n     * @param clazz  the Class to query for\n     * @return A list of type T containing objects of type clazz\n     */\n    @SuppressWarnings(\"unchecked\")\n    @NotNull static <T extends StoredObject> List<T> query(final Collection<StoredObject> values, final Class<T> clazz) {\n        return values.parallelStream().filter(o -> clazz.isAssignableFrom(o.getClass()))\n                .map(o -> (T) o).collect(Collectors.toList());\n    }\n\n    static XStream configureXStream(final XStreamJVM9 xstream) {\n\n        // configure XStream security\n        xstream.addPermission(NoTypePermission.NONE);\n        xstream.addPermission(PrimitiveTypePermission.PRIMITIVES);\n        xstream.addPermission(ArrayTypePermission.ARRAYS);\n        xstream.addPermission(new WildcardTypePermission(new String[] {\"java.**\", \"jgnash.engine.**\"}));      \n\n        xstream.ignoreUnknownElements();    // gracefully ignore fields in the file that do not have object members\n\n        xstream.setMode(XStream.ID_REFERENCES);\n\n        xstream.alias(\"date\", LocalDate.class); // use date instead of local-date by default\n\n        xstream.alias(\"Decimal\", BigDecimal.class);\n\n        xstream.alias(\"Account\", Account.class);\n        xstream.alias(\"RootAccount\", RootAccount.class);\n        xstream.alias(\"Budget\", Budget.class);\n        xstream.alias(\"BudgetGoal\", BudgetGoal.class);\n        xstream.alias(\"Config\", Config.class);\n        xstream.alias(\"CurrencyNode\", CurrencyNode.class);\n        xstream.alias(\"ExchangeRate\", ExchangeRate.class);\n        xstream.alias(\"ExchangeRateHistoryNode\", ExchangeRateHistoryNode.class);\n        xstream.alias(\"InvestmentTransaction\", InvestmentTransaction.class);\n        xstream.alias(\"BudgetPeriod\", Period.class);\n        xstream.alias(\"SecurityNode\", SecurityNode.class);\n        xstream.alias(\"SecurityHistoryNode\", SecurityHistoryNode.class);\n        xstream.alias(\"SecurityHistoryEvent\", SecurityHistoryEvent.class);\n        xstream.alias(\"Tag\", Tag.class);\n        xstream.alias(\"Transaction\", Transaction.class);\n        xstream.alias(\"TransactionEntry\", TransactionEntry.class);\n        xstream.alias(\"TransactionEntryAddX\", TransactionEntryAddX.class);\n        xstream.alias(\"TransactionEntryBuyX\", TransactionEntryBuyX.class);\n        xstream.alias(\"TransactionEntryDividendX\", TransactionEntryDividendX.class);\n        xstream.alias(\"TransactionEntryMergeX\", TransactionEntryMergeX.class);\n        xstream.alias(\"TransactionEntryReinvestDivX\", TransactionEntryReinvestDivX.class);\n        xstream.alias(\"TransactionEntryRemoveX\", TransactionEntryRemoveX.class);\n        xstream.alias(\"TransactionEntrySellX\", TransactionEntrySellX.class);\n        xstream.alias(\"TransactionEntrySplitX\", TransactionEntrySplitX.class);\n\n        xstream.useAttributeFor(Account.class, \"placeHolder\");\n        xstream.useAttributeFor(Account.class, \"locked\");\n        xstream.useAttributeFor(Account.class, \"visible\");\n        xstream.useAttributeFor(Account.class, \"name\");\n        xstream.useAttributeFor(Account.class, DESCRIPTION);\n\n        xstream.useAttributeFor(Budget.class, DESCRIPTION);\n        xstream.useAttributeFor(Budget.class, \"name\");\n        xstream.useAttributeFor(Budget.class, \"roundingScale\");\n        xstream.useAttributeFor(Budget.class, \"roundingMode\");\n        xstream.useAttributeFor(Budget.class, \"startMonth\");\n\n        xstream.useAttributeFor(CommodityNode.class, \"symbol\");\n        xstream.useAttributeFor(CommodityNode.class, \"scale\");\n        xstream.useAttributeFor(CommodityNode.class, \"prefix\");\n        xstream.useAttributeFor(CommodityNode.class, \"suffix\");\n        xstream.useAttributeFor(CommodityNode.class, DESCRIPTION);\n\n        xstream.useAttributeFor(SecurityHistoryNode.class, \"date\");\n        xstream.useAttributeFor(SecurityHistoryNode.class, \"price\");\n        xstream.useAttributeFor(SecurityHistoryNode.class, \"high\");\n        xstream.useAttributeFor(SecurityHistoryNode.class, \"low\");\n        xstream.useAttributeFor(SecurityHistoryNode.class, \"volume\");\n\n        xstream.useAttributeFor(SecurityHistoryEvent.class, \"date\");\n        xstream.useAttributeFor(SecurityHistoryEvent.class, \"type\");\n        xstream.useAttributeFor(SecurityHistoryEvent.class, \"value\");\n\n        xstream.useAttributeFor(StoredObject.class, \"uuid\");\n\n        xstream.useAttributeFor(Tag.class, \"name\");\n        xstream.useAttributeFor(Tag.class, \"color\");\n        xstream.useAttributeFor(Tag.class, \"unicode\");\n\n        xstream.omitField(StoredObject.class, \"markedForRemoval\");\n\n        // Ignore fields required for JPA\n        xstream.omitField(StoredObject.class, \"version\");\n\n        xstream.omitField(AmortizeObject.class, \"id\");\n        xstream.omitField(BudgetGoal.class, \"id\");\n        xstream.omitField(TransactionEntry.class, \"id\");\n        xstream.omitField(ExchangeRateHistoryNode.class, \"id\");\n        xstream.omitField(SecurityHistoryNode.class, \"id\");\n        xstream.omitField(SecurityHistoryEvent.class, \"id\");\n\n        // Filters out the hibernate\n        xstream.registerConverter(new HibernateProxyConverter());\n        xstream.registerConverter(new HibernatePersistentCollectionConverter(xstream.getMapper()));\n        xstream.registerConverter(new HibernatePersistentMapConverter(xstream.getMapper()));\n        \n        // These will trigger an illegal reflective access operation \n        //xstream.registerConverter(new HibernatePersistentSortedMapConverter(xstream.getMapper()));\n        //xstream.registerConverter(new HibernatePersistentSortedSetConverter(xstream.getMapper()));\n\n        return xstream;\n    }\n\n    boolean acquireFileLock() {\n        return fileLocker.acquireLock(path);\n    }\n\n    void releaseFileLock() {\n        fileLocker.release();\n    }\n\n    abstract void commit();\n\n    boolean set(final StoredObject object) {\n\n        boolean result = false;\n\n        readWriteLock.writeLock().lock();\n\n        try {\n            if (get(object.getUuid()) == null) { // make sure the UUID is unique before adding\n                objects.add(object);\n            }\n            result = true;\n        } catch (final Exception ex) {\n            Logger.getLogger(AbstractXStreamContainer.class.getName()).log(Level.SEVERE, null, ex);\n        } finally {\n            readWriteLock.writeLock().unlock();\n        }\n\n        return result;\n    }\n\n    void delete(final StoredObject object) {\n        readWriteLock.writeLock().lock();\n\n        try {\n            objects.remove(object);\n        } finally {\n            readWriteLock.writeLock().unlock();\n        }\n    }\n\n    StoredObject get(final UUID uuid) {\n        StoredObject result = null;\n\n        Lock l = readWriteLock.readLock();\n        l.lock();\n\n        try {\n            for (StoredObject o : objects) {\n                if (o.getUuid().equals(uuid)) {\n                    result = o;\n                }\n            }\n        } finally {\n            l.unlock();\n        }\n\n        return result;\n    }\n\n    <T extends StoredObject> List<T> query(final Class<T> clazz) {\n        readWriteLock.readLock().lock();\n\n        try {\n            return query(objects, clazz);\n        } finally {\n            readWriteLock.readLock().unlock();\n        }\n    }\n\n    void close() {\n        releaseFileLock();\n    }\n\n    String getFileName() {\n        if (path != null) {\n            return path.toString();\n        }\n        return null;\n    }\n\n    /**\n     * Returns of list of all {@code StoredObjects} held within this container. The returned list is a defensive\n     * copy.\n     *\n     * @return A list of all {@code StoredObjects}\n     * @see jgnash.engine.StoredObject\n     */\n    List<StoredObject> asList() {\n        readWriteLock.readLock().lock();\n\n        try {\n            return new ArrayList<>(objects);\n        } finally {\n            readWriteLock.readLock().unlock();\n        }\n    }\n\n    static class XStreamOut extends XStreamJVM9 {\n\n        XStreamOut(final ReflectionProvider reflectionProvider, final HierarchicalStreamDriver hierarchicalStreamDriver) {\n            super(reflectionProvider, hierarchicalStreamDriver);\n        }\n\n        @Override\n        protected MapperWrapper wrapMapper(final MapperWrapper next) {\n            return new HibernateMapper(next);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/AbstractXStreamDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.dao.AbstractDAO;\nimport jgnash.engine.dao.DAO;\nimport jgnash.util.NotNull;\n\n/**\n * Simple object container for StoredObjects that reads and writes and xml file.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractXStreamDAO extends AbstractDAO implements DAO {\n\n    /**\n     * Maximum time in seconds before a commit will occur.\n     */\n    static final int MAX_COMMIT_TIME = 30; // seconds\n\n    static final AtomicInteger commitCount = new AtomicInteger(0);\n\n    final AbstractXStreamContainer container;\n\n    private static final ReentrantLock commitLock = new ReentrantLock();\n\n    private static final int MAX_COMMIT_COUNT = 250;\n\n    AbstractXStreamDAO(@NotNull final AbstractXStreamContainer container) {\n        Objects.requireNonNull(container);\n\n        this.container = container;\n    }\n\n    private StoredObject getObjectByUuid(final UUID uuid) {\n        return container.get(uuid);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> T getObjectByUuid(final Class<T> clazz, final UUID uuid) {\n        Object o = getObjectByUuid(uuid);\n\n        if (o != null && clazz.isAssignableFrom(o.getClass())) {\n            return (T) o;\n        }\n\n        return null;\n    }\n\n    final void commit() {\n        dirtyFlag.set(true);\n\n        if (commitCount.getAndIncrement() >= MAX_COMMIT_COUNT) {\n            commitAndReset();\n        }\n    }\n\n    final void commitAndReset() {\n        commitLock.lock();\n\n        try {\n            commitCount.set(0);\n            container.commit();\n        } finally {\n            commitLock.unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/BinaryContainer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardOpenOption;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.DoubleConsumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.Config;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.StoredObjectComparator;\nimport jgnash.engine.Tag;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.util.NotNull;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;\nimport com.thoughtworks.xstream.io.binary.BinaryStreamDriver;\n\n/**\n * Simple object container for StoredObjects that reads and writes a binary file\n * using XStream.\n *\n * @author Craig Cavanaugh\n */\nclass BinaryContainer extends AbstractXStreamContainer {\n\n    BinaryContainer(final Path path) {\n        super(path);\n    }\n\n    @Override\n    void commit() {\n        writeBinary();\n    }\n\n    private synchronized void writeBinary() {\n        readWriteLock.readLock().lock();\n\n        try {\n            releaseFileLock();\n            writeBinary(objects, path, ignored -> { });\n        } finally {\n            if (!acquireFileLock()) { // lock the file on open\n                Logger.getLogger(BinaryContainer.class.getName()).severe(\"Could not acquire the file lock\");\n            }\n            readWriteLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Writes an XML file given a collection of StoredObjects. TrashObjects and\n     * objects marked for removal are not written. If the file already exists,\n     * it will be overwritten.\n     *\n     * @param objects Collection of StoredObjects to write\n     * @param path    file to write\n     */\n    static synchronized void writeBinary(@NotNull final Collection<StoredObject> objects, @NotNull final Path path,\n                                         @NotNull final DoubleConsumer percentCompleteConsumer) {\n\n        final Logger logger = Logger.getLogger(BinaryContainer.class.getName());\n\n        if (!Files.exists(path.getParent())) {\n            try {\n                Files.createDirectories(path.getParent());\n                logger.info(\"Created missing directories\");\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }\n\n        percentCompleteConsumer.accept(0);\n\n        createBackup(path);\n\n        List<StoredObject> list = new ArrayList<>();\n\n        list.addAll(query(objects, Budget.class));\n        list.addAll(query(objects, Config.class));\n        list.addAll(query(objects, CommodityNode.class));\n        list.addAll(query(objects, ExchangeRate.class));\n        list.addAll(query(objects, RootAccount.class));\n        list.addAll(query(objects, Reminder.class));\n        list.addAll(query(objects, Tag.class));\n\n        percentCompleteConsumer.accept(0.25);\n\n        // remove any objects marked for removal\n        list.removeIf(StoredObject::isMarkedForRemoval);\n\n        // sort the list\n        list.sort(new StoredObjectComparator());\n\n        percentCompleteConsumer.accept(0.5);\n\n        logger.info(\"Writing Binary file\");\n\n        try (final OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {\n\n            final XStream xstream = configureXStream(new XStreamOut(new PureJavaReflectionProvider(),\n                    new BinaryStreamDriver()));\n\n            try (final ObjectOutputStream out = xstream.createObjectOutputStream(os)) {\n                out.writeObject(list);\n                out.flush();\n            }\n\n            os.flush(); // forcibly flush before letting go of the resources to help older windows systems write correctly\n        } catch (IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        logger.info(\"Writing Binary file complete\");\n\n        percentCompleteConsumer.accept(1);\n    }\n\n    void readBinary() {\n\n        // A file lock will be held on Windows OS when reading\n        try (final InputStream fis = new BufferedInputStream(Files.newInputStream(path, StandardOpenOption.READ))) {\n            readWriteLock.writeLock().lock();\n\n            final XStream xstream = configureXStream(new XStreamJVM9(new StoredObjectReflectionProvider(objects),\n                    new BinaryStreamDriver()));\n\n            try (final ObjectInputStream in = xstream.createObjectInputStream(fis)) {\n                in.readObject();\n            }\n\n        } catch (final IOException | ClassNotFoundException e) {\n            Logger.getLogger(BinaryContainer.class.getName()).log(Level.SEVERE, null, e);\n        } finally {\n            if (!acquireFileLock()) { // lock the file on open\n                Logger.getLogger(BinaryContainer.class.getName()).severe(\"Could not acquire the file lock\");\n            }\n            readWriteLock.writeLock().unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/BinaryXStreamDataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.DoubleConsumer;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Config;\nimport jgnash.engine.DataStore;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.attachment.LocalAttachmentManager;\nimport jgnash.engine.concurrent.LocalLockManager;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * XML specific code for data storage and creating an engine.\n *\n * @author Craig Cavanaugh\n */\npublic class BinaryXStreamDataStore implements DataStore {\n\n    private static final Logger logger = Logger.getLogger(BinaryXStreamDataStore.class.getName());\n\n    public static final String FILE_EXT = \".bxds\";\n\n    private BinaryContainer container;\n\n    /**\n     * Close the open {@code Engine}.\n     *\n     * @see jgnash.engine.DataStore#closeEngine()\n     */\n    @Override\n    public void closeEngine() {\n        container.commit(); // force a commit\n        container.close();\n\n        container = null;\n    }\n\n    /**\n     * Create an engine instance that uses a local XML file.\n     *\n     * @see jgnash.engine.DataStore#getLocalEngine(String, String, char[])\n     */\n    @Override\n    public Engine getLocalEngine(final String fileName, final String engineName, final char[] password) {\n\n        Path path = Paths.get(fileName);\n\n        container = new BinaryContainer(path);\n\n        if (Files.exists(path)) {\n            container.readBinary();\n        }\n\n        Engine engine = new Engine(new XStreamEngineDAO(container), new LocalLockManager(),\n                new LocalAttachmentManager(), engineName);\n\n        logger.info(\"Created local Binary container and engine\");\n\n        return engine;\n    }\n\n    /**\n     * {@code XMLDataStore} will always return false.\n     *\n     * @see jgnash.engine.DataStore#isLocal()\n     */\n    @Override\n    public boolean isLocal() {\n        return true;\n    }\n\n    /**\n     * Returns the default file extension for this {@code DataStore}.\n     *\n     * @see jgnash.engine.DataStore#getFileExt()\n     * @see BinaryXStreamDataStore#FILE_EXT\n     */\n    @Override\n    @NotNull\n    public final String getFileExt() {\n        return FILE_EXT;\n    }\n\n    /**\n     * Returns the full path to the file the DataStore is using.\n     *\n     * @see jgnash.engine.DataStore#getFileName()\n     */\n    @Override\n    public final String getFileName() {\n        return container.getFileName();\n    }\n\n    @Override\n    public DataStoreType getType() {\n        return DataStoreType.BINARY_XSTREAM;\n    }\n\n    /**\n     * XMLDataStore will throw an exception if called.\n     *\n     * @see jgnash.engine.DataStore#getClientEngine(String, int, char[], String)\n     * @throws UnsupportedOperationException thrown if an attempt is made to use as a remote data store\n     */\n    @Override\n    public Engine getClientEngine(final String host, final int port, final char[] password, final String engineName) {\n        throw new UnsupportedOperationException(\"Client / Server operation not supported for this type.\");\n    }\n\n    /**\n     * Returns the string representation of this {@code DataStore}.\n     *\n     * @return string representation of this {@code DataStore}.\n     */\n    @Override\n    public String toString() {\n        return ResourceUtils.getString(\"DataStoreType.Bxds\");\n    }\n\n    /*\n     * @see jgnash.engine.DataStore#saveAs(java.util.Collection)\n     */\n    @Override\n    public void saveAs(final Path path, final Collection<StoredObject> objects, final DoubleConsumer percentComplete) {\n        BinaryContainer.writeBinary(objects, path, percentComplete);\n    }\n\n    /**\n     * Opens the file in readonly mode and reads the version of the file format.\n     *\n     * @param file\n     * {@code Path} to open\n     * @return file version\n     */\n    public static float getFileVersion(final Path file) {\n\n        float fileVersion = 0;\n\n        if (Files.exists(file)) {\n            final BinaryContainer container = new BinaryContainer(file);\n\n            try {\n                container.readBinary();\n\n                List<Config> list = container.query(Config.class);\n\n                if (list.size() == 1) {\n                    fileVersion = Float.parseFloat(list.get(0).getFileFormat());\n                } else {\n                    fileVersion = Float.parseFloat(list.get(0).getFileFormat());\n                    logger.severe(\"A duplicate config object was found\");\n                }\n            } finally {\n                container.close();\n            }\n        }\n\n        return fileVersion;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/StoredObjectReflectionProvider.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;\n\nimport java.util.List;\n\nimport jgnash.engine.StoredObject;\n\n/**\n * Expanded XStream reflection provider.\n *\n * This will load all objects that extend {@code StoredObject} into a supplied list as they are created\n *\n * @author Craig Cavanaugh\n */\nfinal class StoredObjectReflectionProvider extends PureJavaReflectionProvider {\n\n    /**\n     * Reference to the supplied list to load objects into.\n     */\n    private final List<StoredObject> objects;\n\n    StoredObjectReflectionProvider(final List<StoredObject> objects) {\n        this.objects = objects;\n    }\n\n    @Override\n    public Object newInstance(final Class type) {\n        Object o = super.newInstance(type);\n\n        if (o instanceof StoredObject) {\n            objects.add((StoredObject) o);\n        }\n        return o;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XMLContainer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.DoubleConsumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.Config;\nimport jgnash.engine.Engine;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.StoredObjectComparator;\nimport jgnash.engine.Tag;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.recurring.Reminder;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.io.xml.StaxDriver;\nimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;\nimport com.thoughtworks.xstream.io.xml.PrettyPrintWriter;\nimport jgnash.util.NotNull;\n\n/**\n * Simple object container for StoredObjects that reads and writes an XML file.\n *\n * @author Craig Cavanaugh\n */\nclass XMLContainer extends AbstractXStreamContainer {\n\n    XMLContainer(final Path path) {\n        super(path);\n    }\n\n    /**\n     * Writes an XML file given a collection of StoredObjects. TrashObjects and\n     * objects marked for removal are not written. If the file already exists,\n     * it will be overwritten.\n     *\n     * @param objects Collection of StoredObjects to write\n     * @param path    file to write\n     */\n    static synchronized void writeXML(@NotNull final Collection<StoredObject> objects, @NotNull final Path path,\n                                      @NotNull final DoubleConsumer percentCompleteConsumer) {\n\n        Logger logger = Logger.getLogger(XMLContainer.class.getName());\n\n        if (!Files.exists(path.getParent())) {\n            try {\n                Files.createDirectories(path.getParent());\n                logger.info(\"Created missing directories\");\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }\n\n        percentCompleteConsumer.accept(0);\n\n        createBackup(path);\n\n        List<StoredObject> list = new ArrayList<>();\n\n        list.addAll(query(objects, Budget.class));\n        list.addAll(query(objects, Config.class));\n        list.addAll(query(objects, CommodityNode.class));\n        list.addAll(query(objects, ExchangeRate.class));\n        list.addAll(query(objects, RootAccount.class));\n        list.addAll(query(objects, Reminder.class));\n        list.addAll(query(objects, Tag.class));\n\n        percentCompleteConsumer.accept(0.25);\n\n        // remove any objects marked for removal\n        list.removeIf(StoredObject::isMarkedForRemoval);\n\n        // sort the list\n        list.sort(new StoredObjectComparator());\n\n        percentCompleteConsumer.accept(0.5);\n\n        logger.info(\"Writing XML file\");\n\n        try (final Writer writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {\n            writer.write(\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\");\n            writer.write(\"<?fileFormat \" + Engine.CURRENT_MAJOR_VERSION + \".\" + Engine.CURRENT_MINOR_VERSION + \"?>\\n\");\n\n            final XStream xstream = configureXStream(new XStreamOut(new PureJavaReflectionProvider(), new StaxDriver()));\n\n            try (final ObjectOutputStream out = xstream.createObjectOutputStream(new PrettyPrintWriter(writer))) {\n                out.writeObject(list);\n                out.flush();     // forcibly flush before letting go of the resources to help older windows systems write correctly\n            } catch (final Exception e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        logger.info(\"Writing XML file complete\");\n\n        percentCompleteConsumer.accept(1);\n    }\n\n    @Override\n    void commit() {\n        writeXML();\n    }\n\n    private synchronized void writeXML() {\n        readWriteLock.readLock().lock();\n\n        try {\n            releaseFileLock();\n            writeXML(objects, path, ignored -> { });\n        } finally {\n            if (!acquireFileLock()) { // lock the file on open\n                Logger.getLogger(XMLContainer.class.getName()).severe(\"Could not acquire the file lock\");\n            }\n            readWriteLock.readLock().unlock();\n        }\n    }\n\n    void readXML() {\n\n        // A file lock will be held on Windows OS when reading\n        try (final Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {\n            readWriteLock.writeLock().lock();\n\n            final XStream xstream = configureXStream(new XStreamJVM9(new StoredObjectReflectionProvider(objects),\n                    new StaxDriver()));\n\n            try (final ObjectInputStream in = xstream.createObjectInputStream(reader)) {\n                in.readObject();\n            }\n\n        } catch (final IOException | ClassNotFoundException e) {\n            Logger.getLogger(XMLContainer.class.getName()).log(Level.SEVERE, null, e);\n        } finally {\n            if (!acquireFileLock()) { // lock the file on open\n                Logger.getLogger(XMLContainer.class.getName()).severe(\"Could not acquire the file lock\");\n            }\n            readWriteLock.writeLock().unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XMLDataStore.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.DoubleConsumer;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Config;\nimport jgnash.engine.DataStore;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.attachment.LocalAttachmentManager;\nimport jgnash.engine.concurrent.LocalLockManager;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * XML specific code for data storage and creating an engine.\n *\n * @author Craig Cavanaugh\n */\npublic class XMLDataStore implements DataStore {\n\n    private static final Logger logger = Logger.getLogger(XMLDataStore.class.getName());\n\n    public static final String FILE_EXT = \".xml\";\n\n    private XMLContainer container;\n\n    /**\n     * Close the open {@code Engine}.\n     *\n     * @see DataStore#closeEngine()\n     */\n    @Override\n    public void closeEngine() {\n        container.commit(); // force a commit\n        container.close();\n\n        container = null;\n    }\n\n    /**\n     * Create an engine instance that uses a local XML file.\n     *\n     * @see DataStore#getLocalEngine(java.lang.String, java.lang.String, char[])\n     */\n    @Override\n    public Engine getLocalEngine(final String fileName, final String engineName, final char[] password) {\n\n        final Path path = Paths.get(fileName);\n\n        container = new XMLContainer(path);\n\n        if (Files.exists(path)) {\n            container.readXML();\n        }\n\n        Engine engine = new Engine(new XStreamEngineDAO(container), new LocalLockManager(),\n                new LocalAttachmentManager(), engineName);\n\n        logger.info(\"Created local XML container and engine\");\n\n        return engine;\n    }\n\n    /**\n     * {@code XMLDataStore} will always return false.\n     *\n     * @see DataStore#isLocal()\n     */\n    @Override\n    public boolean isLocal() {\n        return true;\n    }\n\n    /**\n     * Returns the default file extension for this {@code DataStore}.\n     *\n     * @see DataStore#getFileExt()\n     * @see XMLDataStore#FILE_EXT\n     */\n    @NotNull\n    @Override\n    public final String getFileExt() {\n        return FILE_EXT;\n    }\n\n    /**\n     * Returns the full path to the file the DataStore is using.\n     *\n     * @see DataStore#getFileName()\n     */\n    @Override\n    public final String getFileName() {\n        return container.getFileName();\n    }\n\n    @Override\n    public DataStoreType getType() {\n        return DataStoreType.XML;\n    }\n\n    /**\n     * XMLDataStore will throw an exception if called.\n     *\n     * @see DataStore#getClientEngine(java.lang.String, int, char[], java.lang.String)\n     * @throws UnsupportedOperationException thrown if an attempt is made to use as a remote data store\n     */\n    @Override\n    public Engine getClientEngine(final String host, final int port, final char[] password, final String engineName) {\n        throw new UnsupportedOperationException(\"Client / Server operation not supported for this type.\");\n    }\n\n    /**\n     * Returns the string representation of this {@code DataStore}.\n     *\n     * @return string representation of this\n     * {@code DataStore}.\n     */\n    @Override\n    public String toString() {\n        return ResourceUtils.getString(\"DataStoreType.XML\");\n    }\n\n    @Override\n    public void saveAs(final Path path, final Collection<StoredObject> objects, final DoubleConsumer percentComplete) {\n        XMLContainer.writeXML(objects, path, percentComplete);\n    }\n\n    /**\n     * Opens the file in readonly mode and reads the version of the file format.\n     *\n     * @param file\n     * {@code File} to open\n     * @return file version\n     */\n    public static float getFileVersion(final Path file) {\n\n        float fileVersion = 0;\n\n        if (Files.exists(file)) {\n            final XMLContainer container = new XMLContainer(file);\n\n            try {\n                container.readXML();\n\n                List<Config> list = container.query(Config.class);\n\n                if (list.size() == 1) {\n                    fileVersion = Float.parseFloat(list.get(0).getFileFormat());\n                } else {\n                    fileVersion = Float.parseFloat(list.get(0).getFileFormat());\n                    Logger.getLogger(XMLDataStore.class.getName()).severe(\"A duplicate config object was found\");\n                }\n            } finally {\n                container.close();\n            }\n        }\n\n        return fileVersion;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamAccountDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.dao.AccountDAO;\n\n/**\n * XML Account DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamAccountDAO extends AbstractXStreamDAO implements AccountDAO {\n\n    private static final Logger logger = Logger.getLogger(XStreamAccountDAO.class.getName());\n\n    XStreamAccountDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public RootAccount getRootAccount() {\n        RootAccount root = null;\n\n        List<RootAccount> list = container.query(RootAccount.class);\n\n        if (list.size() == 1) {\n            root = list.get(0);\n        }\n\n        if (list.size() > 1) {  // old bug\n            logger.severe(\"More than one RootAccount found\");\n\n            for (final RootAccount rootAccount : list) {\n                if (rootAccount.getChildCount() > 0) {\n                    root = rootAccount;\n                }\n            }\n        }\n\n        return root;\n    }\n\n    @Override\n    public List<Account> getAccountList() {\n        return stripMarkedForRemoval(container.query(Account.class));\n    }\n\n    @Override\n    public boolean addAccount(final Account parent, final Account child) {\n        container.set(child);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public boolean addRootAccount(final RootAccount account) {\n        container.set(account);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public boolean addAccountSecurity(final Account account, final SecurityNode node) {\n        container.set(node);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public List<Account> getIncomeAccountList() {\n        return getAccountByType(AccountType.INCOME);\n    }\n\n    @Override\n    public List<Account> getExpenseAccountList() {\n        return getAccountByType(AccountType.EXPENSE);\n    }\n\n    @Override\n    public List<Account> getInvestmentAccountList() {\n        return getAccountByType(AccountType.INVEST);\n    }\n\n    @Override\n    public Account getAccountByUuid(final UUID uuid) {\n        return getObjectByUuid(Account.class, uuid);\n    }\n\n    @Override\n    public boolean updateAccount(final Account account) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public boolean toggleAccountVisibility(final Account account) {\n        commit();\n        return true;\n    }\n\n    private List<Account> getAccountByType(final AccountType type) {\n        return getAccountList().parallelStream().filter(a -> a.getAccountType() == type).collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamBudgetDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.dao.BudgetDAO;\n\nimport java.util.List;\nimport java.util.UUID;\n\n/**\n * XML Budget DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamBudgetDAO extends AbstractXStreamDAO implements BudgetDAO {\n\n    XStreamBudgetDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public boolean add(final Budget budget) {\n        container.set(budget);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public boolean update(final Budget budget) {\n        container.set(budget);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public List<Budget> getBudgets() {\n        return stripMarkedForRemoval(container.query(Budget.class));\n    }\n\n    @Override\n    public Budget getBudgetByUuid(final UUID uuid) {\n        return getObjectByUuid(Budget.class, uuid);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamCommodityDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.dao.CommodityDAO;\nimport jgnash.util.NotNull;\n\n/**\n * Hides all the db4o commodity code.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamCommodityDAO extends AbstractXStreamDAO implements CommodityDAO {\n\n    XStreamCommodityDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public boolean addCommodity(final CommodityNode node) {\n        boolean result = container.set(node);\n        commit();\n        return result;\n    }\n\n    @Override\n    public boolean addExchangeRateHistory(final ExchangeRate rate) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public boolean addSecurityHistory(final SecurityNode node, final SecurityHistoryNode historyNode) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public boolean addSecurityHistoryEvent(final SecurityNode node, final SecurityHistoryEvent historyEvent) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public Set<CurrencyNode> getActiveCurrencies() {\n        Set<CurrencyNode> set = stripMarkedForRemoval(container.query(Account.class))\n                .parallelStream().map(Account::getCurrencyNode).collect(Collectors.toSet());\n\n        set.addAll(stripMarkedForRemoval(container.query(SecurityNode.class))\n                .parallelStream().map(SecurityNode::getReportedCurrencyNode).collect(Collectors.toList()));\n\n        return set;\n    }\n\n    @Override\n    public List<CurrencyNode> getCurrencies() {\n        return stripMarkedForRemoval(container.query(CurrencyNode.class));\n    }\n\n    @Override\n    public CurrencyNode getCurrencyByUuid(final UUID uuid) {\n        return getObjectByUuid(CurrencyNode.class, uuid);\n    }\n\n    @Override\n    public SecurityNode getSecurityByUuid(final UUID uuid) {\n        return getObjectByUuid(SecurityNode.class, uuid);\n    }\n\n    @Override\n    public ExchangeRate getExchangeNode(final String rateId) {\n        ExchangeRate rate = null;\n\n        for (ExchangeRate r : stripMarkedForRemoval(container.query(ExchangeRate.class))) {\n            if (r.getRateId().equals(rateId)) {\n                rate = r;\n                break;\n            }\n        }\n\n        return rate;\n    }\n\n    @Override\n    public ExchangeRate getExchangeRateByUuid(final UUID uuid) {\n        ExchangeRate exchangeRate = null;\n\n        StoredObject o = container.get(uuid);\n\n        if (o instanceof ExchangeRate) {\n            exchangeRate = (ExchangeRate) o;\n        }\n\n        return exchangeRate;\n    }\n\n    @Override\n    @NotNull public List<SecurityNode> getSecurities() {\n        return stripMarkedForRemoval(container.query(SecurityNode.class));\n    }\n\n    @Override\n    public List<ExchangeRate> getExchangeRates() {\n        return stripMarkedForRemoval(container.query(ExchangeRate.class));\n    }\n\n    @Override\n    public boolean removeExchangeRateHistory(final ExchangeRate rate) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public boolean removeSecurityHistory(final SecurityNode node, final SecurityHistoryNode historyNode) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public boolean removeSecurityHistoryEvent(final SecurityNode node, final SecurityHistoryEvent historyEvent) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public void addExchangeRate(final ExchangeRate eRate) {\n        container.set(eRate);\n        commit();\n    }\n\n    @Override\n    public boolean updateCommodityNode(final CommodityNode node) {\n        commit();\n        return true;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamConfigDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Config;\nimport jgnash.engine.dao.ConfigDAO;\n\n/**\n * Config object DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamConfigDAO extends AbstractXStreamDAO implements ConfigDAO {\n\n    private static final Logger logger = Logger.getLogger(XStreamConfigDAO.class.getName());\n\n    XStreamConfigDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public Config getDefaultConfig() {\n        Config defaultConfig = null;\n\n        List<Config> list = container.query(Config.class);\n\n        if (!list.isEmpty()) {\n            defaultConfig = list.get(0);\n        }\n\n        if (defaultConfig == null) {\n            defaultConfig = new Config();\n            container.set(defaultConfig);\n            commit();\n            logger.info(\"Generating new default config\");\n        }\n\n        return defaultConfig;\n    }\n\n    @Override\n    public void update(final Config config) {\n        container.set(config);\n        commit();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamEngineDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.List;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.StoredObject;\nimport jgnash.engine.dao.AccountDAO;\nimport jgnash.engine.dao.BudgetDAO;\nimport jgnash.engine.dao.CommodityDAO;\nimport jgnash.engine.dao.ConfigDAO;\nimport jgnash.engine.dao.EngineDAO;\nimport jgnash.engine.dao.RecurringDAO;\nimport jgnash.engine.dao.TagDAO;\nimport jgnash.engine.dao.TransactionDAO;\nimport jgnash.engine.dao.TrashDAO;\n\n/**\n * XML Engine DAO Interface.\n * \n * @author Craig Cavanaugh\n */\nclass XStreamEngineDAO extends AbstractXStreamDAO implements EngineDAO {\n\n    private AccountDAO accountDAO;\n\n    private BudgetDAO budgetDAO;\n\n    private CommodityDAO commodityDAO;\n\n    private ConfigDAO configDAO;\n\n    private RecurringDAO recurringDAO;\n\n    private TagDAO tagDAO;\n\n    private TransactionDAO transactionDAO;\n\n    private TrashDAO trashDAO;\n\n    private ScheduledExecutorService commitExecutor;\n\n    private ScheduledFuture<?> future;\n\n    private final Timer commitTimer;\n\n    XStreamEngineDAO(final AbstractXStreamContainer container) {\n        super(container);\n\n        commitTimer = new Timer();\n\n        // scheduled thread to check and verify a commit occurs every 30 seconds at the minimum if needed.\n        commitExecutor = Executors.newSingleThreadScheduledExecutor();\n\n        // run commit every 30 seconds, 30 seconds after startup\n        commitTimer.scheduleAtFixedRate(new TimerTask() {\n\n            @Override\n            public void run() {\n\n                if (future != null && !future.isDone() || commitExecutor == null) {\n                    return;\n                }\n\n                future = commitExecutor.schedule(() -> {\n                    if (commitCount.get() > 0) {\n                        Logger.getLogger(XStreamEngineDAO.class.getName()).info(\"Committing file\");\n                        commitAndReset();\n                    }\n                }, 0, TimeUnit.SECONDS);\n            }\n        }, MAX_COMMIT_TIME * 1000, MAX_COMMIT_TIME * 1000);\n    }\n\n    @Override\n    public synchronized void shutdown() {\n        commitTimer.cancel();\n        commitExecutor.shutdown();\n        commitExecutor = null;\n    }\n\n    @Override\n    public synchronized AccountDAO getAccountDAO() {\n        if (accountDAO == null) {\n            accountDAO = new XStreamAccountDAO(container);\n        }\n        return accountDAO;\n    }\n\n    @Override\n    public BudgetDAO getBudgetDAO() {\n        if (budgetDAO == null) {\n            budgetDAO = new XStreamBudgetDAO(container);\n        }\n        return budgetDAO;\n    }\n\n    @Override\n    public synchronized CommodityDAO getCommodityDAO() {\n        if (commodityDAO == null) {\n            commodityDAO = new XStreamCommodityDAO(container);\n        }\n        return commodityDAO;\n    }\n\n    @Override\n    public synchronized ConfigDAO getConfigDAO() {\n        if (configDAO == null) {\n            configDAO = new XStreamConfigDAO(container);\n        }\n        return configDAO;\n    }\n\n    @Override\n    public synchronized RecurringDAO getRecurringDAO() {\n        if (recurringDAO == null) {\n            recurringDAO = new XStreamRecurringDAO(container);\n        }\n        return recurringDAO;\n    }\n\n    @Override\n    public synchronized TransactionDAO getTransactionDAO() {\n        if (transactionDAO == null) {\n            transactionDAO = new XStreamTransactionDAO(container);\n        }\n        return transactionDAO;\n    }\n\n    @Override\n    public TagDAO getTagDAO() {\n        if (tagDAO == null) {\n            tagDAO = new XStreamTagDAO(container);\n        }\n        return tagDAO;\n    }\n\n    @Override\n    public synchronized TrashDAO getTrashDAO() {\n        if (trashDAO == null) {\n            trashDAO = new XStreamTrashDAO(container);\n        }\n        return trashDAO;\n    }\n\n    @Override\n    public List<StoredObject> getStoredObjects() {\n        return container.asList();\n    }\n\n    @Override\n    public <T extends StoredObject> List<T> getStoredObjects(Class<T> tClass) {\n        return stripMarkedForRemoval(container.query(tClass));\n    }\n\n    @Override\n    public void refresh(final StoredObject object) {\n        // do nothing for XStream\n    }\n\n    @Override\n    public void bulkUpdate(List<? extends StoredObject> objectList) {\n        commit();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamJVM9.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.converters.Converter;\nimport com.thoughtworks.xstream.converters.basic.BigDecimalConverter;\nimport com.thoughtworks.xstream.converters.basic.BigIntegerConverter;\nimport com.thoughtworks.xstream.converters.basic.BooleanConverter;\nimport com.thoughtworks.xstream.converters.basic.ByteConverter;\nimport com.thoughtworks.xstream.converters.basic.CharConverter;\nimport com.thoughtworks.xstream.converters.basic.DateConverter;\nimport com.thoughtworks.xstream.converters.basic.DoubleConverter;\nimport com.thoughtworks.xstream.converters.basic.FloatConverter;\nimport com.thoughtworks.xstream.converters.basic.IntConverter;\nimport com.thoughtworks.xstream.converters.basic.LongConverter;\nimport com.thoughtworks.xstream.converters.basic.NullConverter;\nimport com.thoughtworks.xstream.converters.basic.ShortConverter;\nimport com.thoughtworks.xstream.converters.basic.StringConverter;\nimport com.thoughtworks.xstream.converters.collections.ArrayConverter;\nimport com.thoughtworks.xstream.converters.collections.CharArrayConverter;\nimport com.thoughtworks.xstream.converters.collections.CollectionConverter;\nimport com.thoughtworks.xstream.converters.collections.MapConverter;\nimport com.thoughtworks.xstream.converters.enums.EnumConverter;\nimport com.thoughtworks.xstream.converters.extended.EncodedByteArrayConverter;\nimport com.thoughtworks.xstream.converters.reflection.ExternalizableConverter;\nimport com.thoughtworks.xstream.converters.reflection.ReflectionConverter;\nimport com.thoughtworks.xstream.converters.reflection.ReflectionProvider;\nimport com.thoughtworks.xstream.converters.reflection.SerializableConverter;\nimport com.thoughtworks.xstream.converters.time.InstantConverter;\nimport com.thoughtworks.xstream.converters.time.LocalDateConverter;\nimport com.thoughtworks.xstream.converters.time.LocalDateTimeConverter;\nimport com.thoughtworks.xstream.converters.time.LocalTimeConverter;\nimport com.thoughtworks.xstream.core.util.SelfStreamingInstanceChecker;\nimport com.thoughtworks.xstream.io.HierarchicalStreamDriver;\nimport com.thoughtworks.xstream.converters.basic.UUIDConverter;\n\n/**\n * This Replaces the default XStream constructor to prevent the loading of converters that trigger an illegal reflective \n * access operation when operating with JVM 9 or newer.\n * \n * @author Craig Cavanaugh\n */\npublic class XStreamJVM9 extends XStream {\n\n\tpublic XStreamJVM9(final ReflectionProvider reflectionProvider, final HierarchicalStreamDriver hierarchicalStreamDriver) {\n\t\tsuper(reflectionProvider, hierarchicalStreamDriver);\n\t}\t\n\n\t@Override\n\tprotected void setupConverters() {\n\t\t\t\t\n\t\tregisterConverter(new ReflectionConverter(getMapper(), getReflectionProvider()), PRIORITY_VERY_LOW);\n\t\tregisterConverter(new SerializableConverter(getMapper(), getReflectionProvider(), getClassLoaderReference()), PRIORITY_LOW);\n\t\tregisterConverter(new ExternalizableConverter(getMapper(), getClassLoaderReference()), PRIORITY_LOW);\n\n\t\tregisterConverter(new NullConverter(), PRIORITY_VERY_HIGH);\n\t\tregisterConverter(new IntConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new FloatConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new DoubleConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new LongConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new ShortConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter((Converter) new CharConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new BooleanConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new ByteConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new StringConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new DateConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new BigIntegerConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new BigDecimalConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new ArrayConverter(getMapper()), PRIORITY_NORMAL);\n\t\tregisterConverter(new CharArrayConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new CollectionConverter(getMapper()), PRIORITY_NORMAL);\n\t\tregisterConverter(new MapConverter(getMapper()), PRIORITY_NORMAL);\n\t\tregisterConverter((Converter) new EncodedByteArrayConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new EnumConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new InstantConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new LocalDateConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new LocalDateTimeConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new LocalTimeConverter(), PRIORITY_NORMAL);\n\t\tregisterConverter(new UUIDConverter(), PRIORITY_NORMAL);\n\n\t\tregisterConverter(new SelfStreamingInstanceChecker(getConverterLookup(), this), PRIORITY_NORMAL);\t\t\t\t\n\t}\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamRecurringDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport jgnash.engine.dao.RecurringDAO;\nimport jgnash.engine.recurring.Reminder;\n\nimport java.util.List;\nimport java.util.UUID;\n\n/**\n * Recurring XML DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamRecurringDAO extends AbstractXStreamDAO implements RecurringDAO {\n\n    XStreamRecurringDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public List<Reminder> getReminderList() {\n        return stripMarkedForRemoval(container.query(Reminder.class));\n    }\n\n    @Override\n    public boolean addReminder(final Reminder reminder) {\n        container.set(reminder);\n        commit();\n        return true;\n    }\n\n    @Override\n    public Reminder getReminderByUuid(final UUID uuid) {\n        return getObjectByUuid(Reminder.class, uuid);\n    }\n\n    @Override\n    public boolean updateReminder(final Reminder reminder) {\n        commit();\n        return true;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamTagDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport jgnash.engine.Tag;\nimport jgnash.engine.dao.TagDAO;\n\n/**\n * XML Tag DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamTagDAO extends AbstractXStreamDAO implements TagDAO {\n\n    XStreamTagDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public boolean add(final Tag tag) {\n        container.set(tag);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public boolean update(final Tag tag) {\n        container.set(tag);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public Set<Tag> getTags() {\n        return new HashSet<>(stripMarkedForRemoval(container.query(Tag.class)));\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamTransactionDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport jgnash.engine.Transaction;\nimport jgnash.engine.dao.TransactionDAO;\n\n/**\n * Transaction XML DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamTransactionDAO extends AbstractXStreamDAO implements TransactionDAO {\n\n    XStreamTransactionDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public List<Transaction> getTransactions() {\n        return stripMarkedForRemoval(container.query(Transaction.class));\n    }\n\n    @Override\n    public boolean addTransaction(final Transaction transaction) {\n        container.set(transaction);\n        commit();\n\n        return true;\n    }\n\n    @Override\n    public Transaction getTransactionByUuid(final UUID uuid) {\n        return getObjectByUuid(Transaction.class, uuid);\n    }\n\n    @Override\n    public boolean removeTransaction(final Transaction transaction) {\n        commit();\n        return true;\n    }\n\n    @Override\n    public List<Transaction> getTransactionsWithAttachments() {\n        return container.query(Transaction.class).parallelStream()\n                .filter(transaction -> !transaction.isMarkedForRemoval() && transaction.getAttachment() != null)\n                .collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/engine/xstream/XStreamTrashDAO.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.xstream;\n\nimport jgnash.engine.TrashObject;\nimport jgnash.engine.dao.TrashDAO;\n\nimport java.util.List;\nimport java.util.logging.Logger;\n\n/**\n * XML trash DAO.\n *\n * @author Craig Cavanaugh\n */\nclass XStreamTrashDAO extends AbstractXStreamDAO implements TrashDAO {\n\n    private static final Logger logger = Logger.getLogger(XStreamTrashDAO.class.getName());\n\n    XStreamTrashDAO(final AbstractXStreamContainer container) {\n        super(container);\n    }\n\n    @Override\n    public List<TrashObject> getTrashObjects() {\n        return container.query(TrashObject.class);\n    }\n\n    @Override\n    public void add(final TrashObject trashObject) {\n        container.set(trashObject);\n        commit();\n    }\n\n    @Override\n    public void remove(final TrashObject trashObject) {\n        container.delete(trashObject.getObject());\n        container.delete(trashObject);\n\n        commit();\n\n        logger.info(\"Removed TrashObject\");\n    }\n\n    @Override\n    public void addEntityTrash(Object entity) {\n        // XStream does not need to do anything with entity trash\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/AbstractAuthenticator.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net;\n\nimport java.net.Authenticator;\nimport java.util.prefs.Preferences;\n\n/**\n * An Authenticator that will pop up a dialog and ask for http authentication\n * info if it has not assigned. This does not make authentication information\n * permanent. That must be done using the options configuration for http connect\n *\n * @author Craig Cavanaugh\n */\npublic class AbstractAuthenticator extends Authenticator {\n\n    public static final String NODEHTTP = \"/jgnash/http\";\n\n    protected static final String HTTPUSER = \"httpuser\";\n\n    protected static final String HTTPPASS = \"httppass\";\n\n    public static final String USEPROXY = \"useproxy\";\n\n    private static final String USEAUTH = \"useauth\";\n\n    public static final String PROXYHOST = \"proxyhost\";\n\n    public static final String PROXYPORT = \"proxyport\";\n\n    public static void setName(String name) {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        pref.put(HTTPUSER, name);\n    }\n\n    public static String getName() {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        return pref.get(HTTPUSER, \"user\");\n    }\n\n    public static void setHost(String host) {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        pref.put(PROXYHOST, host);\n    }\n\n    public static String getHost() {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        return pref.get(PROXYHOST, \"localhost\");\n    }\n\n    public static void setPort(int port) {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        pref.putInt(PROXYPORT, port);\n    }\n\n    public static int getPort() {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        return pref.getInt(PROXYPORT, 8080);\n    }\n\n    public static void setPassword(String password) {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        pref.put(HTTPPASS, password);\n    }\n\n    public static String getPassword() {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        return pref.get(HTTPPASS, \"\");\n    }\n\n    public static void setUseAuthentication(boolean use) {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        pref.putBoolean(USEAUTH, use);\n    }\n\n    public static void setUseProxy(boolean use) {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        pref.putBoolean(USEPROXY, use);\n    }\n\n    public static boolean isProxyUsed() {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        return pref.getBoolean(USEPROXY, false);\n    }\n\n    public static boolean isAuthenticationUsed() {\n        final Preferences pref = Preferences.userRoot().node(NODEHTTP);\n        return pref.getBoolean(USEAUTH, false);\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/ConnectionFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net;\n\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.prefs.Preferences;\n\nimport jgnash.util.Nullable;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Factory methods for setting network connection timeout and creating a URLConnection with timeout set correctly.\n * \n * @author Craig Cavanaugh\n */\npublic class ConnectionFactory {\n\n    private static final String TIMEOUT = \"timeout\";\n\n    private static final int DEFAULT_TIMEOUT = 30;\n\n    public static final int MAX_TIMEOUT = 30;\n\n    public static final int MIN_TIMEOUT = 1;\n\n    public static final int MILLIS_PER_SECOND = 1000;\n\n    private ConnectionFactory() {\n    }\n\n    /**\n     * Sets the network timeout in seconds.\n     * \n     * @param seconds time in seconds before a timeout occurs\n     */\n    public synchronized static void setConnectionTimeout(final int seconds) {\n\n        if (seconds < MIN_TIMEOUT || seconds > MAX_TIMEOUT) {\n            throw new IllegalArgumentException(\"Invalid timeout connection\");\n        }\n\n        Preferences pref = Preferences.userNodeForPackage(ConnectionFactory.class);\n        pref.putInt(TIMEOUT, seconds);\n    }\n\n    /**\n     * Return the network connection timeout in seconds.\n     * \n     * @return timeout in seconds\n     */\n    public synchronized static int getConnectionTimeout() {\n        Preferences pref = Preferences.userNodeForPackage(ConnectionFactory.class);\n        return pref.getInt(TIMEOUT, DEFAULT_TIMEOUT);\n    }\n\n    @Nullable\n    public synchronized static URLConnection openConnection(final String url) {\n        try {\n            return openConnection(new URL(url));\n        } catch (final MalformedURLException ex) {\n            logSevere(ConnectionFactory.class, ex);\n        }\n        return null;\n    }\n\n    @Nullable\n    private synchronized static URLConnection openConnection(final URL url) {\n        URLConnection connection = null;\n\n        try {\n            connection = url.openConnection();\n        } catch (final IOException ex) {\n            logSevere(ConnectionFactory.class, ex);\n        }\n\n        /* Set the connection timeout */\n        if (connection != null) {\n            connection.setConnectTimeout(getConnectionTimeout() * 1000);\n            connection.setReadTimeout(getConnectionTimeout() * 1000);\n        }\n\n        return connection;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/YahooCrumbManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.URLConnection;\nimport java.nio.charset.StandardCharsets;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.TemporalAccessor;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.prefs.Preferences;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport jgnash.util.LogUtil;\n\n/**\n * Manages the Cookie and Crumb required to connect to the yahoo finance APIs.\n * <p>\n * The cookie and crumb is cached and reused\n *\n * @author Craig Cavanaugh\n */\npublic class YahooCrumbManager {\n\n    private static final String SET_COOKIE_KEY = \"Set-Cookie\";\n\n    private static final String CRUMB_REGEX = \".*\\\"CrumbStore\\\":\\\\{\\\"crumb\\\":\\\"(?<crumb>.+?)\\\"}.*\";\n\n    private static final String EXPIRES = \"expires\";\n\n    private static final String CRUMB = \"crumb\";\n\n    private static final String COOKIE = \"cookie\";\n\n    private static final String DATE_FORMAT = \"dd-MMM-yyyy HH:mm:ss z\";\n\n    private static String cookie;\n\n    private static String crumb;\n\n    private YahooCrumbManager() {\n        // utility class\n    }\n\n    /**\n     * This scrapes the cookie and crumb from a Yahoo web page.\n     * <p>\n     * This must be call prior to requesting a cookie or crumb.\n     *\n     * @param symbol Stock to use to scrape value\n     */\n    public static synchronized boolean authorize(final String symbol) {\n        boolean result = false;\n\n        final Preferences preferences = Preferences.userNodeForPackage(YahooCrumbManager.class);\n\n        if (preferences.get(EXPIRES, null) != null) {\n\n            final ZonedDateTime expires = ZonedDateTime.parse(preferences.get(EXPIRES, null));\n\n            if (ZonedDateTime.now().compareTo(expires) < 0) {\n                crumb = preferences.get(CRUMB, null);\n                cookie = preferences.get(COOKIE, null);\n\n                if (cookie != null && crumb != null) {\n                    return true;\n                }\n            } else {\n                clearAuthorization();\n            }\n\n        }\n\n        final String url = \"https://finance.yahoo.com/quote/\" + symbol + \"?p=\" + symbol;\n\n        final URLConnection connection = ConnectionFactory.openConnection(url);\n\n        if (connection != null) {\n\n            final Map<String, List<String>> headerFields = connection.getHeaderFields();\n\n            for (final Map.Entry<String, List<String>> entry : headerFields.entrySet()) {\n                if (SET_COOKIE_KEY.equalsIgnoreCase(entry.getKey())) {\n                    final List<String> cookieValue = entry.getValue();\n\n                    if (cookieValue != null) {\n                        final String[] values = cookieValue.get(0).split(\";\");\n                        cookie = values[0];\n\n                        preferences.put(COOKIE, cookie);\n\n                        final String expires = values[1].trim();\n\n                        if (expires.startsWith(\"expires\")) {    // old, left should Yahoo switch back\n                            final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);\n                            final TemporalAccessor accessor = formatter.parse(expires.split(\",\")[1].trim());\n                            preferences.put(EXPIRES, ZonedDateTime.from(accessor).toString());\n                        } else if (expires.startsWith(\"Max-Age\")) {\n                            final long seconds = Long.parseLong(expires.split(\"=\")[1].trim());\n                            ZonedDateTime expireDate = ZonedDateTime.now().plusSeconds(seconds);\n                            preferences.put(EXPIRES, expireDate.toString());\n                        } else {\n                            preferences.put(EXPIRES, null);\n                        }\n\n                        final Pattern pattern = Pattern.compile(CRUMB_REGEX);\n\n                        try (final BufferedReader reader =\n                                     new BufferedReader(new InputStreamReader(connection.getInputStream(),\n                                             StandardCharsets.UTF_8))) {\n\n                            String line;\n\n                            while ((line = reader.readLine()) != null) {\n                                final Matcher matcher = pattern.matcher(line);\n\n                                if (matcher.matches()) {\n                                    crumb = matcher.group(1);\n                                    preferences.put(CRUMB, crumb);\n                                    result = true;\n                                    break;\n                                }\n                            }\n                        } catch (final IOException e) {\n                            LogUtil.logSevere(YahooCrumbManager.class, e);\n                        }\n                    }\n                }\n            }\n        }\n\n        return result;\n    }\n\n    public static synchronized void clearAuthorization() {\n        final Preferences preferences = Preferences.userNodeForPackage(YahooCrumbManager.class);\n\n        preferences.remove(CRUMB);\n        preferences.remove(COOKIE);\n    }\n\n    public static synchronized String getCookie() {\n        return cookie;\n    }\n\n    public static synchronized String getCrumb() {\n        return crumb;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/currency/CurrencyConverterParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.currency;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.math.BigDecimal;\nimport java.net.SocketTimeoutException;\nimport java.net.URLConnection;\nimport java.net.UnknownHostException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.logging.Logger;\n\nimport jgnash.net.ConnectionFactory;\nimport jgnash.util.LogUtil;\n\n/**\n * A CurrencyParser for the CurrencyConverterAPI site.\n *\n * Use www.currencyconverterapi.com\n *\n * @author Pranay Kumar\n */\npublic class CurrencyConverterParser implements CurrencyParser {\n\n    private BigDecimal result = null;\n\n    @Override\n    public synchronized boolean parse(final String source, final String target) {\n\n        String label = source + \"_\" + target;\n\n        /* Build the URL:  https://free.currencyconverterapi.com/api/v6/convert?q=HKD_INR&compact=ultra */\n\n        StringBuilder url = new StringBuilder(\"https://free.currencyconverterapi.com/api/v6/convert?q=\");\n        url.append(label);\n        url.append(\"&compact=ultra\");\n\n        BufferedReader in = null;\n\n        try {\n            URLConnection connection = ConnectionFactory.openConnection(url.toString());\n\n            if (connection != null) {\n\n                in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));\n\n                /* Result: {\"HKD_INR\":9.263368} */\n                StringBuilder resp = new StringBuilder();\n                String l;\n                while ( (l = in.readLine()) != null) {\n                    resp.append(l.trim());\n                }\n\n                resp = new StringBuilder(resp.toString().replace(\"{\", \"\"));\n                resp = new StringBuilder(resp.toString().replace(\"}\", \"\"));\n\n                String[] tokens = resp.toString().split(\":\");\n                if ( (\"\\\"\"+label+\"\\\"\").equals(tokens[0]) ) {\n                    result = new BigDecimal(tokens[1]);\n                }\n            }\n        } catch (final SocketTimeoutException | UnknownHostException e) {\n            result = null;\n            Logger.getLogger(CurrencyConverterParser.class.getName()).warning(e.getLocalizedMessage());\n            return false;\n        } catch (final Exception e) {\n            LogUtil.logSevere(CurrencyConverterParser.class, e);\n            return false;\n        } finally {\n            try {\n                if (in != null) {\n                    in.close();\n                }\n            } catch (final IOException ex) {\n                LogUtil.logSevere(CurrencyConverterParser.class, ex);\n            }\n        }\n\n        return true;\n    }\n\n    @Override\n    public synchronized BigDecimal getConversion() {\n        return result;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/currency/CurrencyParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.currency;\n\nimport java.math.BigDecimal;\n\n/**\n * Currency parser interface.\n *\n * @author Craig Cavanaugh\n */\ninterface CurrencyParser {\n\n    BigDecimal getConversion();\n\n    boolean parse(String source, String target);\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/currency/CurrencyUpdateFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.currency;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\n\n/**\n * Fetches latest exchange rates in the background.\n *\n * @author Craig Cavanaugh\n */\npublic class CurrencyUpdateFactory {\n\n    private static final String UPDATE_ON_STARTUP = \"updateCurrenciesOnStartup\";\n\n    private CurrencyUpdateFactory() {\n    }\n\n    public static void setUpdateOnStartup(final boolean update) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            engine.putBoolean(UPDATE_ON_STARTUP, update);\n        }\n    }\n\n    public static boolean getUpdateOnStartup() {\n        boolean result = false;\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            result = engine.getBoolean(UPDATE_ON_STARTUP, false);\n        }\n\n        return result;\n    }\n\n    public static Optional<BigDecimal> getExchangeRate(final CurrencyNode source, final CurrencyNode target) {\n        Optional<BigDecimal> optional = Optional.empty();\n\n        final CurrencyParser parser = new CurrencyConverterParser();\n\n        if (parser.parse(source.getSymbol(), target.getSymbol())) {\n            final BigDecimal exchangeRate = parser.getConversion();\n\n            if (exchangeRate != null && exchangeRate.compareTo(BigDecimal.ZERO) != 0) {\n                optional = Optional.of(exchangeRate);\n            }\n        }\n\n        return optional;\n    }\n\n    public static class UpdateExchangeRatesCallable implements Callable<Boolean> {\n\n        @Override\n        public Boolean call() {\n\n            boolean result = false;\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (engine != null) {\n                final List<CurrencyNode> list = engine.getCurrencies();\n\n                for (final CurrencyNode source : list) {\n                    list.stream().filter(target -> !source.equals(target)\n                            && source.getSymbol().compareToIgnoreCase(target.getSymbol()) > 0).forEach(target -> {\n\n                        final Optional<BigDecimal> rate = CurrencyUpdateFactory.getExchangeRate(source, target);\n\n                        rate.ifPresent(value -> engine.setExchangeRate(source, target, value));\n                    });\n                }\n                result = true;\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/security/NullParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.net.security;\n\nimport java.time.LocalDate;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.util.NotNull;\n\n/**\n * Null security history parser.\n *\n * Returns empty results\n *\n * @author Craig Cavanaugh\n */\npublic class NullParser implements SecurityParser {\n\n    /**\n     * Sets a {@code Supplier} that can provide an API token when requested.\n     *\n     * @param supplier token {@code Supplier}\n     */\n    @Override\n    public void setTokenSupplier(Supplier<String> supplier) {\n        // do nothing...\n    }\n\n    @Override\n    public List<SecurityHistoryNode> retrieveHistoricalPrice(final SecurityNode securityNode, final LocalDate startDate, final LocalDate endDate) {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public Set<SecurityHistoryEvent> retrieveHistoricalEvents(@NotNull final SecurityNode securityNode, final LocalDate endDate) {\n        return Collections.emptySet();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/security/SecurityParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.security;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.util.NotNull;\n\n/**\n * Interface for security parser.\n *\n * @author Craig Cavanaugh\n */\npublic interface SecurityParser {\n\n    /**\n     * Sets a {@code Supplier} that can provide an API token when requested.\n     *\n     * @param supplier token {@code Supplier}\n     */\n    void setTokenSupplier(@NotNull final Supplier<String> supplier);\n\n    /**\n     * Retrieves historical pricing\n     *\n     * @param securityNode SecurityNode to retrieve events for\n     * @param startDate    start date\n     * @param endDate      end date\n     * @return List of SecurityHistoryNode\n     * @throws IOException indicates if IO / Network error has occurred\n     */\n    @NotNull\n    List<SecurityHistoryNode> retrieveHistoricalPrice(@NotNull final SecurityNode securityNode,\n                                                      final LocalDate startDate, final LocalDate endDate) throws IOException;\n\n    /**\n     * Retrieves historical events\n     *\n     * @param securityNode SecurityNode to retrieve events for\n     * @param endDate      end date\n     * @return Set of SecurityHistoryEvent\n     * @throws IOException indicates if IO / Network error has occurred\n     */\n    @NotNull\n    Set<SecurityHistoryEvent> retrieveHistoricalEvents(@NotNull final SecurityNode securityNode,\n                                                       final LocalDate endDate) throws IOException;\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/security/UpdateFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.security;\n\nimport java.io.IOException;\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.function.Supplier;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.QuoteSource;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.net.security.iex.IEXParser;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.LogUtil;\nimport jgnash.util.NotNull;\n\n/**\n * Fetches latest stock prices in the background.\n *\n * @author Craig Cavanaugh\n */\npublic class UpdateFactory {\n\n    private static final String UPDATE_ON_STARTUP = \"updateSecuritiesOnStartup\";\n\n    // static reference is kept so LogManager cannot garbage collect the logger\n    private static final Logger logger = Logger.getLogger(UpdateFactory.class.getName());\n\n    private static final int TIMEOUT = 1;   // default timeout in minutes\n\n    /**\n     * Registers a {@code Handler} with the class logger.\n     *\n     * @param handler {@code Handler} to register\n     */\n    public static void addLogHandler(final Handler handler) {\n        logger.addHandler(handler);\n    }\n\n    public static void setUpdateOnStartup(final boolean update) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            engine.putBoolean(UPDATE_ON_STARTUP, update);\n        }\n    }\n\n    /**\n     * Determines if an automatic update is recommended.\n     * <p>\n     * The current approach is to avoid multiple updates on Saturday or Sunday if one has already occurred.\n     * This could be expanded to understand locale rules.\n     *\n     * @param lastUpdate the last known timestamp for an update to have occurred\n     * @return true if an update is recommended\n     */\n    public static boolean shouldAutomaticUpdateOccur(final LocalDateTime lastUpdate) {\n        boolean result = true;\n\n        final LocalDate lastDate = LocalDate.from(lastUpdate);\n        final DayOfWeek lastDayOfWeek = lastDate.getDayOfWeek();\n\n        if (lastDayOfWeek == DayOfWeek.SATURDAY || lastDayOfWeek == DayOfWeek.SUNDAY) {\n            if (LocalDate.now().equals(lastDate) ||\n                    (LocalDate.now().minusDays(1).equals(lastDate)) && lastDayOfWeek == DayOfWeek.SATURDAY) {\n                result = false;\n            }\n        }\n\n        if (result && LocalDate.now().equals(lastDate)) {   // check for an after hours update\n            switch (Locale.getDefault().getCountry()) {\n                case \"AU\":\n                case \"CA\":\n                case \"HK\":\n                case \"US\":\n                    final ZonedDateTime zdtUS = lastUpdate.atZone(ZoneId.of(\"UTC\").normalized());\n                    if (zdtUS.getHour() >= 21 && zdtUS.getMinute() > 25) {  // 4:25 EST for delayed online sources\n                        result = false;\n                    }\n                    break;\n                case \"GB\":  // UK\n                    final ZonedDateTime zdtUK = lastUpdate.atZone(ZoneId.of(\"UTC\").normalized());\n                    if (zdtUK.getHour() >= 21 && zdtUK.getMinute() > 55) {  // 4:55 EST for delayed online sources\n                        result = false;\n                    }\n                    break;\n                case \"IN\":  // India\n                    final ZonedDateTime zdtIN = lastUpdate.atZone(ZoneId.of(\"UTC\").normalized());\n                    if (zdtIN.getHour() >= 20 && zdtIN.getMinute() > 55) {  // 3:55 EST for delayed online sources\n                        result = false;\n                    }\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        return result;\n    }\n\n    public static boolean getUpdateOnStartup() {\n        boolean result = false;\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            result = engine.getBoolean(UPDATE_ON_STARTUP, false);\n        }\n\n        return result;\n    }\n\n    public static void updateSecurityEvents(final SecurityNode node) {\n        waitForCallable(new UpdateSecurityNodeEventsCallable(node));\n    }\n\n    public static boolean updateOne(final SecurityNode node) {\n        return waitForCallable(new UpdateSecurityNodeCallable(node));\n    }\n\n    private static boolean waitForCallable(final Callable<Boolean> callable) {\n        boolean result = false;\n\n        final ExecutorService service = Executors.newSingleThreadExecutor();\n        final Future<Boolean> future = service.submit(callable);\n\n        try {\n            result = future.get(TIMEOUT, TimeUnit.MINUTES);\n            service.shutdown();\n        } catch (final InterruptedException | ExecutionException e) { // intentionally interrupted\n            logger.log(Level.FINEST, e.getLocalizedMessage(), e);\n        } catch (final TimeoutException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n\n        return result;\n    }\n\n    public static List<SecurityHistoryNode> downloadHistory(final SecurityNode securityNode, final LocalDate startDate,\n                                                            final LocalDate endDate) throws IllegalArgumentException {\n\n        List<SecurityHistoryNode> newSecurityNodes = Collections.emptyList();\n\n        QuoteSource quoteSource = securityNode.getQuoteSource();\n        Objects.requireNonNull(quoteSource);\n\n        SecurityParser securityParser = quoteSource.getParser();\n        Objects.requireNonNull(securityParser);\n\n        securityParser.setTokenSupplier(getTokenSupplier(securityParser));\n\n        try {\n            newSecurityNodes = securityParser.retrieveHistoricalPrice(securityNode,\n                    startDate, endDate);\n\n            if (!newSecurityNodes.isEmpty()) {\n                logger.info(ResourceUtils.getString(\"Message.UpdatedPrice\", securityNode.getSymbol()));\n            }\n\n        } catch (final IOException ex) {\n            LogUtil.logSevere(UpdateFactory.class, ex);\n        } catch (final IllegalArgumentException iae) {\n            LogUtil.logSevere(UpdateFactory.class, iae);\n            throw new IllegalArgumentException(iae);\n        }\n\n        return newSecurityNodes;\n    }\n\n    /**\n     * Updates historical information for one day\n     */\n    public static class UpdateSecurityNodeCallable implements Callable<Boolean> {\n\n        private final SecurityNode securityNode;\n\n        public UpdateSecurityNodeCallable(@NotNull final SecurityNode securityNode) {\n            this.securityNode = securityNode;\n        }\n\n        @Override\n        public Boolean call() {\n            boolean result = true;\n\n            final Engine e = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (e != null) {    // protect against a call in process during a shutdown\n                // check for thread interruption\n                final QuoteSource quoteSource = securityNode.getQuoteSource();\n\n                if (quoteSource != QuoteSource.NONE && !Thread.currentThread().isInterrupted()) {\n\n                    try {\n                        final SecurityParser securityParser = quoteSource.getParser();\n                        Objects.requireNonNull(securityParser);\n\n                        securityParser.setTokenSupplier(getTokenSupplier(securityParser));\n\n                        final List<SecurityHistoryNode> nodes = securityParser.retrieveHistoricalPrice(securityNode,\n                                LocalDate.now().minusDays(1), LocalDate.now());\n\n                        for (final SecurityHistoryNode node : nodes) {\n                            if (!Thread.currentThread().isInterrupted()) { // check for thread interruption\n                                if (e.addSecurityHistory(securityNode, node)) {\n                                    logger.info(ResourceUtils.getString(\"Message.UpdatedPrice\", securityNode.getSymbol()));\n                                }\n                            }\n                        }\n                    } catch (final IOException | IllegalArgumentException ex) {\n                        result = false;\n                        LogUtil.logSevere(UpdateFactory.class, ex);\n                    }\n                }\n            }\n\n            return result;\n        }\n    }\n\n    public static class UpdateSecurityNodeEventsCallable implements Callable<Boolean> {\n\n        private final SecurityNode securityNode;\n\n        public UpdateSecurityNodeEventsCallable(@NotNull final SecurityNode securityNode) {\n            this.securityNode = securityNode;\n        }\n\n        @Override\n        public Boolean call() {\n\n            boolean result = true;\n\n            final Engine e = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            final LocalDate oldest = securityNode.getHistoryNodes().get(0).getLocalDate();\n\n            final QuoteSource quoteSource = securityNode.getQuoteSource();\n            Objects.requireNonNull(quoteSource);\n\n            if (e != null && quoteSource != QuoteSource.NONE) {\n                try {\n                    final Set<SecurityHistoryEvent> oldHistoryEvents = new HashSet<>(securityNode.getHistoryEvents());\n\n                    final SecurityParser securityParser = quoteSource.getParser();\n                    Objects.requireNonNull(securityParser);\n\n                    securityParser.setTokenSupplier(getTokenSupplier(securityParser));\n\n                    for (final SecurityHistoryEvent securityHistoryEvent : securityParser.retrieveHistoricalEvents(securityNode, LocalDate.now())) {\n                        if (!Thread.currentThread().isInterrupted()) { // check for thread interruption\n                            if (securityHistoryEvent.getDate().isAfter(oldest) || securityHistoryEvent.getDate().isEqual(oldest)) {\n                                if (!oldHistoryEvents.contains(securityHistoryEvent)) {\n                                    result = e.addSecurityHistoryEvent(securityNode, securityHistoryEvent);\n\n                                    if (result) {\n                                        logger.info(ResourceUtils.getString(\"Message.UpdatedSecurityEvent\", securityNode.getSymbol()));\n                                    }\n                                }\n                            }\n                        }\n                    }\n                } catch (final IOException | IllegalArgumentException ex) {\n                    result = false;\n                    LogUtil.logSevere(UpdateFactory.class, ex);\n                }\n            }\n\n            return result;\n        }\n    }\n\n    /**\n     * Returns a token Supplier\n     * @param parser parser we need a token supplier for\n     * @return Supplier\n     */\n    private static Supplier<String> getTokenSupplier(final SecurityParser parser) {\n        if (parser instanceof IEXParser) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (engine != null) {\n                return () -> engine.getPreference(IEXParser.IEX_SECRET_KEY);\n            }\n        }\n\n        return () -> \"\";\n    }\n\n    private UpdateFactory() {\n        // Utility class\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/security/YahooEventParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.security;\n\nimport java.io.BufferedReader;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.math.BigDecimal;\nimport java.net.HttpURLConnection;\nimport java.net.URLConnection;\nimport java.nio.charset.StandardCharsets;\nimport java.time.DateTimeException;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.Month;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryEventType;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.net.ConnectionFactory;\nimport jgnash.net.YahooCrumbManager;\nimport jgnash.util.LogUtil;\nimport jgnash.util.NotNull;\n\nimport static jgnash.util.EncodeDecode.COMMA_DELIMITER_PATTERN;\n\n/**\n * Retrieves historical stock dividend and split information from Yahoo.\n *\n * @author Craig Cavanaugh\n */\npublic class YahooEventParser implements SecurityParser {\n\n    private static final String DIV_RESPONSE_HEADER = \"Date,Dividends\";\n\n    private static final String SPLIT_RESPONSE_HEADER = \"Date,Stock Splits\";\n\n    private static final String HISTORY_RESPONSE_HEADER = \"Date,Open,High,Low,Close,Adj Close,Volume\";\n\n    public Set<SecurityHistoryEvent> retrieveHistoricalEvents(@NotNull final SecurityNode securityNode,\n                                                              final LocalDate endDate) throws IOException {\n\n        final Set<SecurityHistoryEvent> events = new HashSet<>();\n\n        // Ensure we have a valid cookie and crumb\n        if (!YahooCrumbManager.authorize(securityNode.getSymbol())) {\n            return events;\n        }\n\n        LocalDate startDate = LocalDate.now().minusDays(1);\n\n        final List<SecurityHistoryNode> historyNodeList = securityNode.getHistoryNodes();\n\n        if (!historyNodeList.isEmpty()) {\n            startDate = historyNodeList.get(0).getLocalDate();\n        }\n\n        events.addAll(retrieveNewDividends(securityNode, startDate, endDate));\n        events.addAll(retrieveNewSplits(securityNode, startDate, endDate));\n\n        return events;\n    }\n\n    private static List<SecurityHistoryEvent> retrieveNewDividends(@NotNull final SecurityNode securityNode,\n                                                                   final LocalDate startDate, final LocalDate endDate) throws IOException {\n\n        /*\n        Date,Dividends\n        1989-02-02,0.275\n        1989-08-03,0.3025\n        1964-02-04,0.00167\n        1964-08-04,0.00208\n        */\n\n        return parseStream(securityNode, startDate, endDate,\n                SecurityHistoryEventType.DIVIDEND, DIV_RESPONSE_HEADER::equals,\n                line -> {\n                    final String[] fields = COMMA_DELIMITER_PATTERN.split(line);\n\n                    if (fields.length == 2) {   // if fields are != 2, then it's not valid data\n                        try {\n                            return new SecurityHistoryEvent(SecurityHistoryEventType.DIVIDEND,\n                                    parseYahooDate(fields[0]), new BigDecimal(fields[1]));\n\n                        } catch (final DateTimeException | NumberFormatException ex) {\n                            Logger.getLogger(YahooEventParser.class.getName()).log(Level.INFO, line);\n                            LogUtil.logSevere(YahooEventParser.class, ex);\n                        }\n                    }\n                    return null;\n                });\n    }\n\n    /**\n     * Sets a {@code Supplier} that can provide an API token when requested.\n     *\n     * @param supplier token {@code Supplier}\n     */\n    @Override\n    public void setTokenSupplier(final Supplier<String> supplier) {\n        // no nothing, API not required for access to Yahoo\n    }\n\n    @Override\n    public List<SecurityHistoryNode> retrieveHistoricalPrice(@NotNull final SecurityNode securityNode,\n                                                             final LocalDate startDate, final LocalDate endDate) throws IOException {\n\n        /*\n         Date,Open,High,Low,Close,Adj Close,Volume\n         2016-01-04,128.344070,128.694244,127.056824,135.949997,128.675323,5229400\n         2016-01-05,129.441956,129.565018,127.634193,135.850006,128.580673,3924800\n         2016-01-06,127.189331,128.325134,126.469986,135.169998,127.937050,4310900\n         */\n\n        final List<SecurityHistoryNode> events = parseStream(securityNode, startDate, endDate,\n                SecurityHistoryEventType.PRICE, HISTORY_RESPONSE_HEADER::equals,\n                line -> {\n                    final String[] fields = COMMA_DELIMITER_PATTERN.split(line);\n\n                    if (fields.length == 7) {   // if fields are != 7, then it's not valid data\n                        try {\n                            // Date,Open,High,Low,Close,Adj Close,Volume\n                            final LocalDate date = parseYahooDate(fields[0]);\n                            final BigDecimal high = new BigDecimal(fields[2]);\n                            final BigDecimal low = new BigDecimal(fields[3]);\n                            final BigDecimal close = new BigDecimal(fields[4]);\n                            final long volume = Long.parseLong(fields[6]);\n\n                            return new SecurityHistoryNode(date, close, volume, high, low);\n                        } catch (final DateTimeException | NumberFormatException ex) {\n                            Logger.getLogger(YahooEventParser.class.getName()).log(Level.INFO, line);\n                            LogUtil.logSevere(YahooEventParser.class, ex);\n\n                        }\n                    }\n                    return null;\n                });\n\n        Collections.reverse(events);    // reverse for better chronological order\n\n        return events;\n    }\n\n\n    private static List<SecurityHistoryEvent> retrieveNewSplits(@NotNull final SecurityNode securityNode,\n                                                                final LocalDate startDate, final LocalDate endDate) throws IOException {\n\n         /*\n        Date,Stock Splits\n        1973-05-29,5/4\n        1964-05-18,5/4\n        1997-05-28,2/1\n        */\n\n        return parseStream(securityNode, startDate, endDate,\n                SecurityHistoryEventType.SPLIT, SPLIT_RESPONSE_HEADER::equals,\n                line -> {\n                    final String[] fields = COMMA_DELIMITER_PATTERN.split(line);\n\n                    if (fields.length == 2) {   // if fields are != 2, then it's not valid data\n                        try {\n\n                            // Yahoo uses : or /\n                            final String delimiter = fields[1].contains(\":\") ? \":\" : \"/\";\n\n                            final String[] fraction = fields[1].split(delimiter);\n\n                            final BigDecimal value = new BigDecimal(fraction[0])\n                                                             .divide(new BigDecimal(fraction[1]), MathConstants.mathContext);\n\n                            return new SecurityHistoryEvent(SecurityHistoryEventType.SPLIT, parseYahooDate(fields[0]),\n                                    value);\n\n                        } catch (final DateTimeException | NumberFormatException ex) {\n                            Logger.getLogger(YahooEventParser.class.getName()).log(Level.INFO, line);\n                            LogUtil.logSevere(YahooEventParser.class, ex);\n                        }\n                    }\n                    return null;\n                });\n\n    }\n\n    private static <T> List<T> parseStream(@NotNull final SecurityNode securityNode, final LocalDate startDate,\n                                           final LocalDate endDate, final SecurityHistoryEventType type,\n                                           final Function<String, Boolean> acceptHeaderFunction,\n                                           final Function<String, T> processLineFunction) throws IOException, NullPointerException {\n\n        final List<T> events = new ArrayList<>();\n\n        // Ensure we have a valid cookie and crumb\n        if (!YahooCrumbManager.authorize(securityNode.getSymbol())) {\n            return events;\n        }\n\n        final String url = buildYahooQuery(securityNode, startDate, endDate, type);\n\n        URLConnection connection = null;\n\n        try {\n            connection = ConnectionFactory.openConnection(url);\n\n            if (connection != null) {\n\n                // required by Yahoo\n                connection.setRequestProperty(\"Cookie\", YahooCrumbManager.getCookie());\n\n                int responseCode = ((HttpURLConnection) connection).getResponseCode();\n\n                if (responseCode == 401) {\n                    YahooCrumbManager.clearAuthorization();\n                } else {\n                    try (final BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(),\n                            StandardCharsets.UTF_8))) {\n\n                        String line = in.readLine();\n\n                        if (acceptHeaderFunction.apply(line)) {\n                            line = in.readLine(); // prime the first read\n\n                            while (line != null) {\n                                if (Thread.currentThread().isInterrupted()) {\n                                    Thread.currentThread().interrupt();\n                                }\n\n                                events.add(processLineFunction.apply(line));\n\n                                line = in.readLine();\n                            }\n                        }\n                    } catch (final FileNotFoundException ignored) {\n                        // silently ignored, history may not exist\n                    }\n                }\n            }\n        } catch (final NullPointerException | IOException ex) {\n            LogUtil.logSevere(YahooEventParser.class, ex);\n            YahooCrumbManager.clearAuthorization();\n            throw new IOException(ex);\n        } finally {\n            if (connection instanceof HttpURLConnection) {\n                ((HttpURLConnection) connection).disconnect();\n            }\n        }\n\n        return events;\n    }\n\n    private static LocalDate parseYahooDate(final String string) {\n        return LocalDate.of(Integer.parseInt(string.substring(0, 4)),\n                Integer.parseInt(string.substring(5, 7)),\n                Integer.parseInt(string.substring(8, 10)));\n    }\n\n\n    private static String buildYahooQuery(final SecurityNode securityNode, final LocalDate startDate,\n                                          final LocalDate endDate, final SecurityHistoryEventType event) {\n        // dividend 1/1/1962 to 8/22/2015\n        // https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=-252442800&period2=1440216000&interval=1d&events=div&crumb=oTulTvLJSBg\n\n        // splits\n        // https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=-252442800&period2=1440216000&interval=1d&events=split&crumb=oTulTvLJSBg\n\n        // History\n        // https://query1.finance.yahoo.com/v7/finance/download/IBM?period1=-252442800&period2=1440216000&interval=1d&events=history&crumb=oTulTvLJSBg\n\n        final LocalDateTime epoch = LocalDateTime.of(1970, Month.JANUARY, 1, 0, 0);\n\n        long period1 = ChronoUnit.SECONDS.between(epoch, LocalDateTime.of(startDate, LocalTime.MIN));\n        long period2 = ChronoUnit.SECONDS.between(epoch, LocalDateTime.of(endDate, LocalTime.MAX));\n\n\n        final StringBuilder builder = new StringBuilder(\"https://query1.finance.yahoo.com/v7/finance/download/\");\n        builder.append(securityNode.getSymbol())\n                .append(\"?period1=\").append(period1)\n                .append(\"&period2=\").append(period2)\n                .append(\"&interval=1d&events=\");\n\n        switch (event) {\n            case DIVIDEND:\n                builder.append(\"div\");\n                break;\n            case PRICE:\n                builder.append(\"history\");\n                break;\n            case SPLIT:\n                builder.append(\"split\");\n                break;\n        }\n\n        builder.append(\"&crumb=\").append(YahooCrumbManager.getCrumb());\n\n        return builder.toString();\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/net/security/iex/IEXParser.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.net.security.iex;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.math.BigDecimal;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Supplier;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryEventType;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.net.ConnectionFactory;\nimport jgnash.net.security.SecurityParser;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.util.LogUtil;\n\nimport org.apache.commons.csv.CSVFormat;\nimport org.apache.commons.csv.CSVParser;\nimport org.apache.commons.csv.CSVRecord;\nimport org.apache.commons.lang3.StringUtils;\n\nimport static java.time.temporal.ChronoUnit.DAYS;\n\n/**\n * SecurityParser for querying iexcloud.io for stock price history and events\n *\n * @author Craig Cavanaugh\n */\npublic class IEXParser implements SecurityParser {\n\n    public static final String IEX_SECRET_KEY = \"IEXSecretKey\";\n\n    private static final String BASE_URL = \"https://cloud.iexapis.com/v1\";\n    private static final String SANDBOX_URL = \"https://sandbox.iexapis.com/stable\";\n    private static final int READ_AHEAD_LIMIT = 512;\n\n    private enum IEXChartPeriod {\n        FiveDay(\"5d\", 5),\n        OneMonth(\"1m\", 30),\n        ThreeMonth(\"3m\", 30 * 3),\n        SixMonth(\"6m\", 30 * 6),\n        OneYear(\"1y\", 365),\n        TwoYear(\"2y\", 365 * 2),\n        FiveYear(\"5y\", 365 * 5),\n        max(\"max\", Integer.MAX_VALUE);\n\n        final private int days;\n        final private String path;\n\n        IEXChartPeriod(final String api, final int days) {\n            this.days = days;\n            this.path = api;\n        }\n\n        public static String getPath(final int days) {\n            for (final IEXChartPeriod period : IEXChartPeriod.values()) {\n                if (period.days > days) {\n                    return period.path;\n                }\n            }\n\n            return IEXChartPeriod.max.path;\n        }\n    }\n\n    /**\n     * Required IEX Token\n     */\n    private Supplier<String> tokenSupplier = () -> \"\";\n\n    private String baseURL = BASE_URL;\n\n    public void setUseSandbox() {\n        baseURL = SANDBOX_URL;\n    }\n\n    /**\n     * Sets a {@code Supplier} that can provide an API token when requested.\n     *\n     * @param supplier token {@code Supplier}\n     */\n    @Override\n    public void setTokenSupplier(final Supplier<String> supplier) {\n        Objects.requireNonNull(supplier);\n        tokenSupplier = supplier;\n    }\n\n    /**\n     * Retrieves historical pricing.\n     * <p>\n     * The IEX API allows query of a period of time starting with the current date.  The startDate is ignored for REST\n     * but is used for filter of the returned data\n     *\n     * @param securityNode SecurityNode to retrieve events for\n     * @param startDate    start date\n     * @param endDate      end date\n     * @return List of SecurityHistoryNode\n     * @throws IOException indicates if IO / Network error has occurred\n     */\n    @Override\n    public List<SecurityHistoryNode> retrieveHistoricalPrice(final SecurityNode securityNode, final LocalDate startDate,\n                                                             final LocalDate endDate) throws IOException, IllegalArgumentException {\n        final String token = tokenSupplier.get();\n\n        if (StringUtils.isBlank(token)) {\n            throw new IllegalArgumentException(ResourceUtils.getString(\"Message.Error.DataSupplierToken\",\n                    securityNode.getSymbol()));\n        }\n\n        final String range = IEXChartPeriod.getPath((int) DAYS.between(startDate, LocalDate.now()));\n\n        final String restURL = baseURL + \"/stock/\" + securityNode.getSymbol() + \"/chart/\" + range +\n                \"?token=\" + token + \"&format=csv\";\n\n        final List<SecurityHistoryNode> historyNodes = new ArrayList<>();\n\n        final URL url = new URL(restURL);\n\n        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();\n        connection.setConnectTimeout(ConnectionFactory.getConnectionTimeout() * 1000);\n\n        // return an empty list if the response is bad\n        if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {\n            connection.disconnect();\n            return historyNodes;\n        }\n\n        try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),\n                StandardCharsets.UTF_8))) {\n\n            reader.mark(READ_AHEAD_LIMIT);\n\n            if (isDataOkay(reader.readLine())) {\n                reader.reset();\n\n                try (final CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) {\n                    for (final CSVRecord record : csvParser) {\n                        final LocalDate date = LocalDate.parse(record.get(\"date\"), DateTimeFormatter.ISO_DATE);\n\n                        if (DateUtils.between(date, startDate, endDate)) {\n                            final BigDecimal price = new BigDecimal(record.get(\"uClose\"));\n                            final long volume = Long.parseLong(record.get(\"uVolume\"));\n                            final BigDecimal high = new BigDecimal(record.get(\"uHigh\"));\n                            final BigDecimal low = new BigDecimal(record.get(\"uLow\"));\n\n                            final SecurityHistoryNode historyNode = new SecurityHistoryNode(date, price, volume, high, low);\n\n                            historyNodes.add(historyNode);\n                        }\n                    }\n                } catch (final IllegalArgumentException iae) {\n                    LogUtil.logSevere(IEXParser.class, iae);\n                }\n            }\n        }\n\n        return historyNodes;\n    }\n\n    /**\n     * Retrieves historical events\n     *\n     * @param securityNode SecurityNode to retrieve events for\n     * @param endDate      end date\n     * @return Set of SecurityHistoryEvent\n     * @throws IOException indicates if IO / Network error has occurred\n     */\n    @Override\n    public Set<SecurityHistoryEvent> retrieveHistoricalEvents(final SecurityNode securityNode,\n                                                              final LocalDate endDate) throws IOException, IllegalArgumentException {\n\n        final String token = tokenSupplier.get();\n\n        if (StringUtils.isBlank(token)) {\n            throw new IllegalArgumentException(ResourceUtils.getString(\"Message.Error.DataSupplierToken\",\n                    securityNode.getSymbol()));\n        }\n\n        final Set<SecurityHistoryEvent> historyEvents = new HashSet<>();\n\n        historyEvents.addAll(retrieveSplitEvents(securityNode, endDate));\n        historyEvents.addAll(retrieveDividendEvents(securityNode, endDate));\n\n        return historyEvents;\n    }\n\n    private Set<SecurityHistoryEvent> retrieveSplitEvents(final SecurityNode securityNode,\n                                                              final LocalDate endDate) throws IOException {\n\n        final String range = IEXChartPeriod.getPath((int) DAYS.between(endDate, LocalDate.now()));\n\n        final String restURL = baseURL + \"/stock/\" + securityNode.getSymbol() + \"/splits/\" + range +\n                \"?token=\" + tokenSupplier.get() + \"&format=csv\";\n\n        final Set<SecurityHistoryEvent> historyEvents = new HashSet<>();\n\n        final URL url = new URL(restURL);\n        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();\n        connection.setConnectTimeout(ConnectionFactory.getConnectionTimeout() * 1000);\n\n        // return an empty list if the response is bad\n        if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {\n            connection.disconnect();\n            return historyEvents;\n        }\n\n        try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),\n                StandardCharsets.UTF_8))) {\n\n            reader.mark(READ_AHEAD_LIMIT);\n\n            if (isDataOkay(reader.readLine())) {\n                reader.reset();\n\n                try (final CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) {\n                    LocalDate startDate = LocalDate.now().minusDays(1);\n\n                    for (final CSVRecord record : csvParser) {\n                        final LocalDate date = LocalDate.parse(record.get(\"exDate\"), DateTimeFormatter.ISO_DATE);\n\n                        if (DateUtils.between(date, endDate, startDate)) {\n                            final BigDecimal toFactor = new BigDecimal(record.get(\"toFactor\"));\n                            final BigDecimal fromFactor = new BigDecimal(record.get(\"fromFactor\"));\n                            final BigDecimal ratio = fromFactor.divide(toFactor, MathConstants.mathContext);\n\n                            final SecurityHistoryEvent event = new SecurityHistoryEvent(SecurityHistoryEventType.SPLIT, date, ratio);\n\n                            historyEvents.add(event);\n                        }\n                    }\n                }\n                catch (final IllegalArgumentException iae) {\n                    LogUtil.logSevere(IEXParser.class, iae);\n                }\n            }\n        }\n\n        return historyEvents;\n    }\n\n    private Set<SecurityHistoryEvent> retrieveDividendEvents(final SecurityNode securityNode,\n                                                          final LocalDate endDate) throws IOException {\n\n        final String range = IEXChartPeriod.getPath((int) DAYS.between(endDate, LocalDate.now()));\n\n        final String restURL = baseURL + \"/stock/\" + securityNode.getSymbol() + \"/dividends/\" + range +\n                \"?token=\" + tokenSupplier.get() + \"&format=csv\";\n\n        //System.out.println(restURL);\n\n        final Set<SecurityHistoryEvent> historyEvents = new HashSet<>();\n\n        final URL url = new URL(restURL);\n        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();\n        connection.setConnectTimeout(ConnectionFactory.getConnectionTimeout() * 1000);\n\n        // return an empty list if the response is bad\n        if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {\n            connection.disconnect();\n            return historyEvents;\n        }\n\n        try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),\n                StandardCharsets.UTF_8))) {\n\n            reader.mark(READ_AHEAD_LIMIT);\n\n            if (isDataOkay(reader.readLine())) {\n                reader.reset();\n\n                try (final CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) {\n                    LocalDate startDate = LocalDate.now().minusDays(1);\n\n                    for (final CSVRecord record : csvParser) {\n                        final LocalDate date = LocalDate.parse(record.get(\"paymentDate\"), DateTimeFormatter.ISO_DATE);\n\n                        if (DateUtils.between(date, endDate, startDate)) {\n                            final BigDecimal amount = new BigDecimal(record.get(\"amount\"));\n\n                            final SecurityHistoryEvent event = new SecurityHistoryEvent(SecurityHistoryEventType.DIVIDEND, date, amount);\n\n                            historyEvents.add(event);\n                        }\n                    }\n                }\n                catch (final IllegalArgumentException iae) {\n                    LogUtil.logSevere(IEXParser.class, iae);\n                }\n            }\n        }\n\n        return historyEvents;\n    }\n\n    /**\n     * Determines if data appears to be okay.... no immediate errors.\n     *\n     * @param line 1st line returned\n     * @return true if data appears to be okay\n     */\n    private static boolean isDataOkay(final String line) {\n\n        if (line != null && !line.isEmpty()) {\n            return !(StringUtils.startsWithIgnoreCase(line, \"Not Found\")\n                    || StringUtils.startsWithIgnoreCase(line, \"Unknown symbol\")\n                    || StringUtils.startsWithIgnoreCase(line, \"An API key is required\")\n                    || StringUtils.startsWithIgnoreCase(line, \"Test tokens may only be used\"));\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/report/BalanceByMonthCSVReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.time.format.TextStyle;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Export monthly balance information as a CSV (comma-separated variable) file.\n *\n * @author Craig Cavanaugh\n * @author Tom Edelson\n */\npublic class BalanceByMonthCSVReport {\n\n    private final List<Account> accountList = new ArrayList<>();\n\n    private final List<BigDecimal[]> balanceList = new ArrayList<>();\n\n    private final boolean vertical;\n\n    private final BiFunction<AccountType, BigDecimal, BigDecimal> balanceConverter;\n\n    private final LocalDate startDate;\n\n    private final LocalDate endDate;\n\n    private final String fileName;\n\n    private final CurrencyNode baseCommodity;\n\n    /**\n     * Report constructor.\n     *\n     * @param fileName         file name to save to\n     * @param startDate        start date for the report\n     * @param endDate          end date for the report\n     * @param currencyNode     CurrencyNode account balances are reported in\n     * @param vertical         {@code true} if the export is oriented vertically instead of horizontally\n     * @param balanceConverter function to adjust account balance sign\n     */\n    public BalanceByMonthCSVReport(@NotNull final String fileName, @NotNull final LocalDate startDate,\n                                   @NotNull final LocalDate endDate, @Nullable final CurrencyNode currencyNode,\n                                   boolean vertical,\n                                   @NotNull final BiFunction<AccountType, BigDecimal, BigDecimal> balanceConverter) {\n\n        Objects.requireNonNull(fileName);\n\n        this.fileName = fileName;\n        this.baseCommodity = currencyNode;\n        this.startDate = startDate;\n        this.endDate = endDate;\n        this.vertical = vertical;\n        this.balanceConverter = balanceConverter;\n    }\n\n    public void run() {\n\n        final Logger logger = Logger.getLogger(BalanceByMonthCSVReport.class.getName());\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final LocalDate[] dates = getLastDays(startDate, endDate);\n\n        buildLists(engine.getRootAccount(), dates);\n\n        try {\n            logger.info(\"Writing file\");\n            if (vertical) {\n                writeVerticalCSVFileFormat(fileName, dates);\n            } else {\n                writeHorizontalFormatCSVFile(fileName, dates);\n            }\n        } catch (IOException e) {\n            logger.log(Level.SEVERE, null, e);\n        }\n    }\n\n    private static LocalDate[] getLastDays(final LocalDate startDate, final LocalDate stopDate) {\n        final ArrayList<LocalDate> list = new ArrayList<>();\n\n        LocalDate t = DateUtils.getLastDayOfTheMonth(startDate);\n\n        // add a month at a time to the previous date until all of the months\n        // have been captured\n        while (DateUtils.before(t, stopDate)) {\n            list.add(t);\n            t = t.plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());\n        }\n\n        return list.toArray(new LocalDate[0]);\n\n    }\n\n    private void buildLists(final Account account, final LocalDate[] dates) {\n        for (final Account child : account.getChildren(Comparators.getAccountByCode())) {\n            if (child.getTransactionCount() > 0) {\n                accountList.add(child); // add the account\n                final BigDecimal[] bigDecimals = new BigDecimal[dates.length];\n                for (int i = 0; i < dates.length; i++) {\n                    if (baseCommodity != null) {\n                        bigDecimals[i] = balanceConverter.apply(child.getAccountType(), child.getBalance(dates[i],\n                                baseCommodity));\n                    } else {\n                        bigDecimals[i] = balanceConverter.apply(child.getAccountType(), child.getBalance(dates[i]));\n                    }\n                }\n                balanceList.add(bigDecimals);\n            }\n            if (child.isParent()) {\n                buildLists(child, dates);\n            }\n        }\n    }\n\n    /*\n     * ,A1,A2,A3 Jan,455,30,80 Feb,566,70,90 March,678,200,300\n     */\n    private void writeHorizontalFormatCSVFile(final String fileName, final LocalDate[] dates) throws IOException {\n\n        if (fileName == null || dates == null) {\n            return;\n        }\n\n        try (final BufferedWriter writer = Files.newBufferedWriter(Paths.get(fileName), StandardCharsets.UTF_8)) {\n\n            // write out the account names with full path\n            final int length = accountList.size();\n\n            for (final Account a : accountList) {\n                writer.write(\",\");\n                writer.write(a.getPathName());\n            }\n\n            writer.newLine();\n\n            // write out the month, and then balance for that month\n            for (int i = 0; i < dates.length; i++) {\n                writer.write(dates[i].getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault()));\n                for (int j = 0; j < length; j++) {\n                    BigDecimal[] bigDecimals = balanceList.get(j);\n                    writer.write(\",\");\n                    writer.write(bigDecimals[i].toString());\n                }\n                writer.newLine();\n            }\n        }\n\n    }\n\n    /*\n     * ,Jan,Feb,Mar A1,30,80,100 A2,70,90,120 A3,200,300,400\n     */\n    private void writeVerticalCSVFileFormat(final String fileName, final LocalDate[] dates) throws IOException {\n\n        if (fileName == null || dates == null) {\n            return;\n        }\n\n        try (final BufferedWriter writer = Files.newBufferedWriter(Paths.get(fileName), StandardCharsets.UTF_8)) {\n\n            // write out the month header, the first column is empty\n            for (final LocalDate date : dates) {\n                writer.write(\",\");\n                writer.write(date.getMonth().getDisplayName(TextStyle.FULL, Locale.getDefault()));\n            }\n\n            writer.newLine();\n\n            // write out the account balance info\n            for (int i = 0; i < accountList.size(); i++) {\n                writer.write(accountList.get(i).getPathName());\n\n                for (final BigDecimal bigDecimal : balanceList.get(i)) {\n                    writer.write(\",\");\n                    writer.write(bigDecimal.toString());\n                }\n                writer.newLine();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/report/ProfitLossTextReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.function.BiFunction;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.text.NumericFormats;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Dumps a simple Profit/Loss report to a text file.\n *\n * @author Michael Mueller\n * @author Craig Cavanaugh\n * @author David Robertson\n */\npublic class ProfitLossTextReport {\n\n    private static final int MAX_LENGTH = 15;  //(-000,000,000.00)\n\n    private static final int MAX_NAME_LEN = 30;\n\n    private static final int HEADER_LENGTH = 54;\n\n    private static final String REPORT_FOOTER = new String(new char[HEADER_LENGTH]).replace(\"\\0\", \"=\");\n\n    private static final String ROW_SEPARATOR = new String(new char[HEADER_LENGTH]).replace(\"\\0\", \"-\");\n\n    private NumberFormat numberFormat;\n\n    private final List<String> reportText = new ArrayList<>();\n\n    private final CurrencyNode baseCommodity;\n\n    private final ResourceBundle rb = ResourceUtils.getBundle();\n\n    private final BiFunction<AccountType, BigDecimal, BigDecimal> balanceConverter;\n\n    private final LocalDate startDate;\n\n    private final LocalDate endDate;\n\n    private final String fileName;\n\n    /**\n     * Report constructor.\n     *\n     * @param fileName         file name to save to\n     * @param startDate        start date for the report\n     * @param endDate          end date for the report\n     * @param currencyNode     CurrencyNode account balances are reported in\n     * @param balanceConverter function to adjust account balance sign\n     */\n    public ProfitLossTextReport(final String fileName, final LocalDate startDate, final LocalDate endDate,\n                                final CurrencyNode currencyNode,\n                                final BiFunction<AccountType, BigDecimal, BigDecimal> balanceConverter) {\n\n        Objects.requireNonNull(fileName);\n\n        this.fileName = fileName;\n        this.baseCommodity = currencyNode;\n        this.startDate = startDate;\n        this.endDate = endDate;\n        this.balanceConverter = balanceConverter;\n    }\n\n    public void run() {\n        generateReport();\n        writeFile();\n    }\n\n    private void generateReport() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Account root = engine.getRootAccount();\n\n        numberFormat = NumericFormats.getFullCommodityFormat(baseCommodity);\n\n        final DateTimeFormatter df = DateTimeFormatter.ISO_DATE;\n\n        // title and dates\n        reportText.add(rb.getString(\"Title.ProfitLoss\"));\n        reportText.add(\"\");\n        reportText.add(\"From \" + df.format(startDate) + \" To \" + df.format(endDate));\n        reportText.add(\"\");\n        reportText.add(\"\");\n\n        //Income\n        reportText.add(AccountType.INCOME.toString());\n        reportText.add(ROW_SEPARATOR);\n\n        //Add up the  Gross Income.\n        BigDecimal incomeTotal = BigDecimal.ZERO;\n\n        for (final BigDecimal balance : getBalances(root, AccountType.INCOME)) {\n            incomeTotal = incomeTotal.add(balance);\n        }\n\n        reportText.add(ROW_SEPARATOR);\n        reportText.add(formatAccountName(rb.getString(\"Word.GrossIncome\")) + \" \" + formatDecimal(incomeTotal));\n        reportText.add(ROW_SEPARATOR);\n        reportText.add(\"\");\n        reportText.add(\"\");\n\n        //Expense\n        reportText.add(AccountType.EXPENSE.toString());\n        reportText.add(ROW_SEPARATOR);\n\n        //Add up the Gross Expenses\n        BigDecimal expenseTotal = BigDecimal.ZERO;\n\n        for (final BigDecimal balance : getBalances(root, AccountType.EXPENSE)) {\n            expenseTotal = expenseTotal.add(balance);\n        }\n        reportText.add(ROW_SEPARATOR);\n        reportText.add(formatAccountName(rb.getString(\"Word.GrossExpense\")) + \" \" + formatDecimal(expenseTotal));\n        reportText.add(ROW_SEPARATOR);\n        reportText.add(\"\");\n        reportText.add(\"\");\n\n        //Net Total\n        reportText.add(ROW_SEPARATOR);\n        reportText.add(formatAccountName(rb.getString(\"Word.NetIncome\")) + \" \" + formatDecimal(incomeTotal.add(expenseTotal)));\n        reportText.add(REPORT_FOOTER);\n    }\n\n    private void writeFile() {\n        try (final BufferedWriter writer = Files.newBufferedWriter(Paths.get(fileName), StandardCharsets.UTF_8)) {\n            for (final String text : reportText) {  //write the array list pl to the file\n                writer.write(text);\n                writer.newLine();\n            }\n            writer.newLine();\n        } catch (IOException e) {\n            Logger.getLogger(ProfitLossTextReport.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private List<BigDecimal> getBalances(final Account a, final AccountType type) {\n\n        final List<BigDecimal> balances = new ArrayList<>();\n\n        for (final Account child : a.getChildren(Comparators.getAccountByCode())) {\n            if ((child.getTransactionCount() > 0) && type == child.getAccountType()) {\n\n                final BigDecimal acctBal = balanceConverter.apply(child.getAccountType(),\n                        child.getBalance(startDate, endDate, baseCommodity));\n\n                // output account name and balance\n                reportText.add(formatAccountName(child.getName()) + \" \" + formatDecimal(acctBal));\n\n                balances.add(acctBal);\n            }\n\n            if (child.isParent()) {\n                balances.addAll(getBalances(child, type));\n            }\n        }\n\n        return balances;\n    }\n\n    /**\n     * Format decimal amount.\n     *\n     * @param balance the BigDecimal value to format\n     * @return formatted string\n     */\n    private String formatDecimal(final BigDecimal balance) {\n        final StringBuilder stringBuilder = new StringBuilder();\n        final String formattedBalance = numberFormat.format(balance);\n\n        // right align amount to pre-defined maximum length\n        if (formattedBalance.length() < MAX_LENGTH) {\n            stringBuilder.append(\" \".repeat(MAX_LENGTH - formattedBalance.length()));\n        }\n\n        stringBuilder.append(formattedBalance);\n\n        return stringBuilder.toString();\n    }\n\n    /**\n     * Format account name.\n     *\n     * @param name the account name to format\n     * @return the formatted account name\n     */\n    private static String formatAccountName(final String name) {\n        final StringBuilder stringBuilder = new StringBuilder(MAX_NAME_LEN);\n\n        stringBuilder.append(name);\n\n        // set name to pre-defined maximum length\n        stringBuilder.append(\" \".repeat(Math.max(0, MAX_NAME_LEN - name.length())));\n\n        stringBuilder.setLength(MAX_NAME_LEN);\n\n        return stringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/report/ReportPeriod.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Report period Enum.\n * \n * @author Craig Cavanaugh\n */\npublic enum ReportPeriod {\n\n    MONTHLY(ResourceUtils.getString(\"Period.Monthly\")),\n    QUARTERLY(ResourceUtils.getString(\"Period.Quarterly\")),\n    YEARLY(ResourceUtils.getString(\"Period.Yearly\"));\n\n    private final transient String description;\n\n    ReportPeriod(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/report/ReportPeriodUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report;\n\nimport java.time.LocalDate;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Utility Methods for reporting.\n *\n * @author Craig Cavanaugh\n */\npublic class ReportPeriodUtils {\n\n    private ReportPeriodUtils() {\n        // utility class\n    }\n\n    public static class Descriptor {\n\n        private final LocalDate startDate;\n        private final LocalDate endDate;\n        private final String label;\n\n        private Descriptor(@NotNull final LocalDate startDate, @NotNull final LocalDate endDate,\n                           @NotNull final String label) {\n            this.startDate = startDate;\n            this.endDate = endDate;\n            this.label = label;\n        }\n\n        public LocalDate getStartDate() {\n            return startDate;\n        }\n\n        public LocalDate getEndDate() {\n            return endDate;\n        }\n\n        public String getLabel() {\n            return label;\n        }\n    }\n\n    public static List<Descriptor> getDescriptors(@NotNull ReportPeriod reportPeriod, @NotNull LocalDate startDate,\n                                                  @NotNull LocalDate endDate) {\n\n        final List<Descriptor> descriptors = new ArrayList<>();\n\n        LocalDate start = startDate;\n        LocalDate end = startDate;\n\n        switch (reportPeriod) {\n            case YEARLY:\n                while (end.isBefore(endDate)) {\n                    end = start.with(TemporalAdjusters.lastDayOfYear());\n                    descriptors.add(new Descriptor(start, end, \"    \" + start.getYear()));\n                    start = end.plusDays(1);\n                }\n                break;\n            case QUARTERLY:\n                int i = DateUtils.getQuarterNumber(start) - 1;\n                while (end.isBefore(endDate)) {\n                    end = DateUtils.getLastDayOfTheQuarter(start);\n                    descriptors.add(new Descriptor(start, end, \" \" + start.getYear() + \"-Q\" + (1 + i++ % 4)));\n                    start = end.plusDays(1);\n                }\n                break;\n            case MONTHLY:\n                while (end.isBefore(endDate)) {\n                    end = DateUtils.getLastDayOfTheMonth(start);\n                    final int month = start.getMonthValue();\n                    descriptors.add(new Descriptor(start, end, \" \" + start.getYear() + (month < 10 ? \"/0\" + month\n                            : \"/\" + month)));\n                    start = end.plusDays(1);\n                }\n                break;\n            default:\n        }\n\n        return descriptors;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/text/NumericFormats.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.text;\n\nimport java.text.DecimalFormat;\nimport java.text.DecimalFormatSymbols;\nimport java.text.NumberFormat;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.WeakHashMap;\nimport java.util.prefs.Preferences;\nimport java.util.regex.Pattern;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.util.NotNull;\n\n/**\n * Utility class to provide Numeric formats\n *\n * @author Craig Cavanaugh\n */\npublic final class NumericFormats {\n\n    private static final String FULL_FORMAT = \"fullFormat\";\n\n    private static final String SHORT_FORMAT = \"shortFormat\";\n\n    private static final CommodityListener listener;\n\n    private static final Map<CommodityNode, ThreadLocal<DecimalFormat>> fullInstanceMap = new WeakHashMap<>();\n\n    private static final Map<CommodityNode, ThreadLocal<DecimalFormat>> simpleInstanceMap = new WeakHashMap<>();\n\n    private static final String CURRENCY_SYMBOL = \"\\u00A4\"; // ¤, must be defined using unicode or some platforms fail test\n\n    static {\n        /*\n         * Need to clear any references to CommodityNodes to prevent memory leaks when\n         * files are loaded and unload.\n         */\n        listener = new CommodityListener();\n\n        MessageBus.getInstance().registerListener(listener, MessageChannel.COMMODITY, MessageChannel.SYSTEM);\n    }\n\n    private NumericFormats() {\n        // factory class\n    }\n\n    public static Set<String> getKnownFullPatterns() {\n\n        Set<String> patternSet = new TreeSet<>();\n\n        for (final Locale locale : Locale.getAvailableLocales()) {\n            DecimalFormat df = (DecimalFormat)NumberFormat.getCurrencyInstance(locale);\n            patternSet.add(df.toPattern());\n        }\n\n        // TODO: add missing US locale format, JDK 11 Bug\n        patternSet.add(\"\\u00A4#,##0.00;(\\u00A4#,##0.00)\");\n        patternSet.add(\"\\u00A4 #,##0.00;(\\u00A4 #,##0.00)\");\n\n        patternSet.add(getFullFormatPattern()); // add the users own format\n\n        return patternSet;\n    }\n\n    public static Set<String> getKnownShortPatterns() {\n\n        final Pattern currencyReplacementPattern = Pattern.compile(CURRENCY_SYMBOL);\n        final Set<String> patternSet = new TreeSet<>();\n\n        for (final Locale locale : Locale.getAvailableLocales()) {\n            final DecimalFormat df = (DecimalFormat)NumberFormat.getCurrencyInstance(locale);\n            final String pattern = df.toPattern();\n\n            patternSet.add(currencyReplacementPattern.matcher(pattern).replaceAll(\"\").stripLeading());\n        }\n\n        patternSet.add(\"#,##0.00;(#,##0.00)\");\n\n        patternSet.add(getShortFormatPattern()); // add the users own format\n\n        return patternSet;\n    }\n\n    public static String getFullFormatPattern() {\n        return getFormatPattern(FULL_FORMAT, ((DecimalFormat)NumberFormat.getCurrencyInstance()).toPattern());\n    }\n\n    public static String getShortFormatPattern() {\n\n        DecimalFormat df = (DecimalFormat)NumberFormat.getCurrencyInstance();\n\n        // create the default short format\n        final DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();\n        dfs.setCurrencySymbol(\"\");\n        df.setDecimalFormatSymbols(dfs);\n\n\n        return getFormatPattern(SHORT_FORMAT, df.toPattern());\n    }\n\n    private static String getFormatPattern(@NotNull final String key, final String defaultPattern) {\n        Objects.requireNonNull(key);\n\n        final Preferences preferences = Preferences.userNodeForPackage(NumericFormats.class);\n        return preferences.get(key, defaultPattern);\n    }\n\n    public static void setFullFormatPattern(@NotNull final String pattern) {\n        if (!getFullFormatPattern().equals(pattern)) {\n            setFormatPattern(FULL_FORMAT, pattern);\n\n            fullInstanceMap.clear();    // flush the cached instance map\n        }\n    }\n\n    public static void setShortFormatPattern(@NotNull final String pattern) {\n        if (!getShortFormatPattern().equals(pattern)) {\n            setFormatPattern(SHORT_FORMAT, pattern);\n\n            simpleInstanceMap.clear();  // flush the cached instance map\n        }\n    }\n\n    private static void setFormatPattern(@NotNull final String key, @NotNull final String pattern) {\n        Objects.requireNonNull(pattern);\n\n        if (!pattern.isBlank()) {\n            final Preferences preferences = Preferences.userNodeForPackage(NumericFormats.class);\n            preferences.put(key, pattern);\n        }\n    }\n\n    /**\n     * Returns a thread safe simplified {@code NumberFormat} for a given {@code CommodityNode}.\n     *\n     * @param node CommodityNode to format to\n     * @return thread safe {@code NumberFormat}\n     */\n    public static NumberFormat getShortCommodityFormat(@NotNull final CommodityNode node) {\n        final ThreadLocal<DecimalFormat> o = simpleInstanceMap.get(node);\n\n        if (o != null) {\n            return o.get();\n        }\n\n        final ThreadLocal<DecimalFormat> threadLocal = ThreadLocal.withInitial(() -> {\n\n            final String pattern = getShortFormatPattern();\n\n            // generate a full currency format\n            if (pattern.contains(CURRENCY_SYMBOL)) {\n                return generateFullFormat(node, pattern);\n            }\n\n            final DecimalFormat df = new DecimalFormat(getShortFormatPattern());\n\n            // required for some locales\n            df.setMaximumFractionDigits(node.getScale());\n            df.setMinimumFractionDigits(df.getMaximumFractionDigits());\n\n            // for positive suffix padding for fraction alignment\n            int negSufLen = df.getNegativeSuffix().length();\n            if (negSufLen > 0) {\n                char[] pad = new char[negSufLen];\n                for (int i = 0; i < negSufLen; i++) {\n                    pad[i] = ' ';\n                }\n                df.setPositiveSuffix(new String(pad));\n            }\n\n            return df;\n        });\n\n        simpleInstanceMap.put(node, threadLocal);\n\n        return threadLocal.get();\n    }\n\n    /**\n     * Returns a thread safe {@code NumberFormat} for a given {@code CommodityNode}.\n     *\n     * @param node CommodityNode to format to\n     * @return thread safe {@code NumberFormat}\n     */\n    public static NumberFormat getFullCommodityFormat(@NotNull final CommodityNode node) {\n        final ThreadLocal<DecimalFormat> o = fullInstanceMap.get(node);\n\n        if (o != null) {\n            return o.get();\n        }\n\n        final ThreadLocal<DecimalFormat> threadLocal = ThreadLocal.withInitial(()\n                -> generateFullFormat(node, getFullFormatPattern()));\n\n        fullInstanceMap.put(node, threadLocal);\n\n        return threadLocal.get();\n    }\n\n    private static DecimalFormat generateFullFormat(final CommodityNode node, final String pattern) {\n        final DecimalFormat df = new DecimalFormat(pattern);\n\n        final DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();\n        dfs.setCurrencySymbol(node.getPrefix());\n        df.setDecimalFormatSymbols(dfs);\n        df.setMaximumFractionDigits(node.getScale());\n\n        // required for some locale\n        df.setMinimumFractionDigits(df.getMaximumFractionDigits());\n\n        if (node.getSuffix() != null && !node.getSuffix().isEmpty()) {\n            df.setPositiveSuffix(node.getSuffix() + df.getPositiveSuffix());\n            df.setNegativeSuffix(node.getSuffix() + df.getNegativeSuffix());\n        }\n\n        // for positive suffix padding for fraction alignment\n        final int negSufLen = df.getNegativeSuffix().length();\n        final int posSufLen = df.getPositiveSuffix().length();\n\n        // pad the prefix and suffix as necessary so that they are the same length\n        if (negSufLen > posSufLen) {\n            df.setPositiveSuffix(df.getPositiveSuffix()\n                    + \" \".repeat(Math.max(0, negSufLen - (negSufLen - posSufLen) + 1)));\n        } else if (posSufLen > negSufLen) {\n            df.setNegativeSuffix(df.getNegativeSuffix()\n                    + \" \".repeat(Math.max(0, posSufLen - (posSufLen - negSufLen) + 1)));\n        }\n\n        return df;\n    }\n\n\n    private static String getConversion(final String cur1, final String cur2) {\n        return cur1 + \" > \" + cur2;\n    }\n\n    public static String getConversion(final CommodityNode cur1, final CommodityNode cur2) {\n        return getConversion(cur1.getSymbol(), cur2.getSymbol());\n    }\n\n    public static NumberFormat getFixedPrecisionFormat(final int scale) {\n        final NumberFormat nf = NumberFormat.getNumberInstance();\n        nf.setMaximumFractionDigits(scale);\n        nf.setMinimumFractionDigits(scale);\n\n        return nf;\n    }\n\n    public static NumberFormat getPercentageFormat() {\n        final NumberFormat nf = NumberFormat.getPercentInstance();\n        nf.setMaximumFractionDigits(2);\n        nf.setMinimumFractionDigits(2);\n\n        return nf;\n    }\n\n    private static class CommodityListener implements MessageListener {\n\n        @Override\n        public void messagePosted(final Message event) {\n            switch (event.getEvent()) {\n                case FILE_CLOSING:\n                case CURRENCY_MODIFY:\n                    simpleInstanceMap.clear();\n                    fullInstanceMap.clear();\n                    break;\n                default:\n                    break;\n            }\n        }\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/time/DateUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.time;\n\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.time.DayOfWeek;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.Month;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeFormatterBuilder;\nimport java.time.format.ResolverStyle;\nimport java.time.temporal.TemporalAdjusters;\nimport java.time.temporal.WeekFields;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.prefs.Preferences;\nimport java.util.regex.Pattern;\n\nimport jgnash.util.NotNull;\n\nimport static java.time.temporal.ChronoField.DAY_OF_MONTH;\nimport static java.time.temporal.ChronoField.HOUR_OF_DAY;\nimport static java.time.temporal.ChronoField.MINUTE_OF_HOUR;\nimport static java.time.temporal.ChronoField.MONTH_OF_YEAR;\nimport static java.time.temporal.ChronoField.SECOND_OF_MINUTE;\nimport static java.time.temporal.ChronoField.YEAR;\n\n/**\n * Static methods to make working with dates a bit easier.\n *\n * @author Craig Cavanaugh\n * @author Vincent Frison\n */\n\npublic class DateUtils {\n\n    private static final int MONTHS_PER_YEAR = 12;\n\n    private static final int DAYS_PER_WEEK = 7;\n\n    private static final String DATE_FORMAT = \"dateFormat\";\n\n    private static final int MILLISECONDS_PER_SECOND = 1000;\n\n    private static final Pattern MONTH_PATTERN = Pattern.compile(\"M{1,2}\");\n\n    private static final Pattern DAY_PATTERN = Pattern.compile(\"d{1,2}\");\n\n    private static final Pattern HOUR_PATTERN = Pattern.compile(\"h{1,2}\");\n\n    private static DateTimeFormatter shortDateFormatter;\n\n    private static final DateTimeFormatter shortDateTimeFormatter;\n\n    private static DateTimeFormatter shortDateManualEntryFormatter;\n\n    static {\n        shortDateFormatter = DateTimeFormatter.ofPattern(getShortDatePattern()).withResolverStyle(ResolverStyle.SMART);\n\n        shortDateTimeFormatter = DateTimeFormatter.ofPattern(getShortDateTimePattern())\n                .withResolverStyle(ResolverStyle.SMART);\n\n        shortDateManualEntryFormatter = DateTimeFormatter.ofPattern(getShortDateManualEntryPattern())\n                .withResolverStyle(ResolverStyle.SMART);\n    }\n\n    private DateUtils() {\n    }\n\n    public static void setShortDateFormatPattern(@NotNull final String pattern) throws IllegalArgumentException {\n        Objects.requireNonNull(pattern);\n        final Preferences preferences = Preferences.userNodeForPackage(DateUtils.class);\n\n        preferences.put(DATE_FORMAT, pattern);\n\n        shortDateFormatter = DateTimeFormatter.ofPattern(pattern).withResolverStyle(ResolverStyle.SMART);\n\n        shortDateManualEntryFormatter = DateTimeFormatter.ofPattern(getShortDateManualEntryPattern())\n                .withResolverStyle(ResolverStyle.SMART);\n    }\n\n    public static Set<String> getAvailableShortDateFormats() {\n        final Set<String> dateFormats = new TreeSet<>();\n\n        for (final Locale locale : Locale.getAvailableLocales()) {\n            final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);\n\n            if (df instanceof SimpleDateFormat) {\n                dateFormats.add(((SimpleDateFormat) df).toPattern());\n            }\n        }\n\n        return dateFormats;\n    }\n\n    public static String getShortDatePattern() {\n        final Preferences preferences = Preferences.userNodeForPackage(DateUtils.class);\n\n        String pattern = preferences.get(DATE_FORMAT, \"\");\n\n        if (pattern.isEmpty()) {    // create a default for the current locale\n            final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);\n\n            if (df instanceof SimpleDateFormat) {\n                pattern = ((SimpleDateFormat) df).toPattern();\n                pattern = DAY_PATTERN.matcher(MONTH_PATTERN.matcher(pattern).replaceAll(\"MM\")).replaceAll(\"dd\");\n            } else {\n                throw new RuntimeException(\"Unexpected class\");\n            }\n        }\n\n        return pattern;\n    }\n\n    private static String getShortDateTimePattern() {\n        final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);\n\n        String pattern = ((SimpleDateFormat) df).toPattern();\n        pattern = DAY_PATTERN.matcher(MONTH_PATTERN.matcher(pattern).replaceAll(\"MM\")).replaceAll(\"dd\");\n        pattern = HOUR_PATTERN.matcher(pattern).replaceAll(\"hh\");\n\n        return pattern;\n    }\n\n    /**\n     * Returns a variant of the default format with required days reduced to one to make manual entry easier.\n     *\n     * @return date format\n     */\n    private static String getShortDateManualEntryPattern() {\n        String pattern = getShortDatePattern();\n\n        // Relax date entry\n        if (pattern.contains(\"dd\")) {\n            pattern = pattern.replace(\"dd\", \"d\");\n        }\n\n        // Relax month entry\n        if (pattern.contains(\"MM\")) {\n            pattern = pattern.replace(\"MM\", \"M\");\n        }\n\n        return pattern;\n    }\n\n    /**\n     * Returns a {@code LocalDate} compatible {@code DateTimeFormatter} that Excel understands\n     * @return Excel compatible {@code DateTimeFormatter}\n     */\n    public static DateTimeFormatter getExcelDateFormatter() {\n        return new DateTimeFormatterBuilder().appendValue(YEAR, 4).appendValue(MONTH_OF_YEAR, 2)\n                .appendValue(DAY_OF_MONTH, 2)\n                .toFormatter();\n    }\n\n    /**\n     * Returns a {@code LocalDateTime} compatible {@code DateTimeFormatter} that Excel understands\n     * @return Excel compatible {@code DateTimeFormatter}\n     */\n    public static DateTimeFormatter getExcelTimestampFormatter() {\n        return new DateTimeFormatterBuilder()\n                .appendValue(YEAR, 4).appendLiteral('-').appendValue(MONTH_OF_YEAR, 2)\n                .appendLiteral('-').appendValue(DAY_OF_MONTH, 2).appendLiteral(' ')\n                .appendValue(HOUR_OF_DAY, 2).appendLiteral(':').appendValue(MINUTE_OF_HOUR, 2)\n                .appendLiteral(':').appendValue(SECOND_OF_MINUTE, 2)\n                .toFormatter();\n    }\n\n    /**\n     * Converts a {@code LocalDate} into a {@code Date} using the default timezone.\n     *\n     * @param localDate {@code LocalDate} to convert\n     * @return an equivalent {@code Date}\n     */\n    public static Date asDate(final LocalDate localDate) {\n        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());\n    }\n\n    /**\n     * Converts a {@code LocalDate} into a {@code Date} using the default timezone.\n     *\n     * @param localDate {@code LocalDate} to convert\n     * @return an equivalent {@code Date}\n     */\n    public static Date asDate(final LocalDateTime localDate) {\n        return Date.from(localDate.atZone(ZoneId.systemDefault()).toInstant());\n    }\n\n    /**\n     * Converts a {@code Date} into a {@code LocalDate} using the default timezone.\n     *\n     * @param date {@code Date} to convert\n     * @return an equivalent {@code LocalDate} or {@code null} if the supplied date was {@code null}\n     */\n    public static LocalDate asLocalDate(final Date date) {\n        if (date != null) {\n            return asLocalDate(date.getTime());\n        }\n\n        return null;\n    }\n\n    /**\n     * Converts milliseconds from the epoch of 1970-01-01T00:00:00Z into a {@code LocalDate} using the default timezone.\n     *\n     * @param milli milliseconds from the epoch of 1970-01-01T00:00:00Z.\n     * @return an equivalent {@code LocalDate} or {@code null} if the supplied date was {@code null}\n     */\n    public static LocalDate asLocalDate(final long milli) {\n        return Instant.ofEpochMilli(milli).atZone(ZoneId.systemDefault()).toLocalDate();\n    }\n\n    /**\n     * Converts a LocaleDate into milliseconds from the epoch of 1970-01-01T00:00:00Z.\n     *\n     * @param localDate {@code LocalDate} to convert\n     * @return and equivalent milliseconds from the epoch of 1970-01-01T00:00:00Z\n     */\n    public static long asEpochMilli(final LocalDate localDate) {\n        return localDate.atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * MILLISECONDS_PER_SECOND;\n    }\n\n    /**\n     * Determines is {@code LocalDate} d1 occurs after {@code LocalDate} d2. The specified dates are\n     * inclusive.\n     *\n     * @param d1 date 1\n     * @param d2 date 2\n     * @return true if d1 is after d2\n     */\n    public static boolean after(final LocalDate d1, final LocalDate d2) {\n        return before(d2, d1, true);\n    }\n\n    /**\n     * Determines if {@code LocalDate} d1 occurs before {@code LocalDate} d2. The specified dates are\n     * inclusive\n     *\n     * @param d1 date 1\n     * @param d2 date 2\n     * @return true if d1 is before d2 or the same date\n     */\n    public static boolean before(final LocalDate d1, final LocalDate d2) {\n        return before(d1, d2, true);\n    }\n\n    /**\n     * Determines if {@code LocalDate} d1 occurs before {@code LocalDate} d2.\n     *\n     * @param d1        {@code LocalDate} 1\n     * @param d2        {@code LocalDate} 2\n     * @param inclusive {@code true} is comparison is inclusive\n     * @return {@code true} if d1 occurs before d2\n     */\n    public static boolean before(final LocalDate d1, final LocalDate d2, final boolean inclusive) {\n        if (inclusive) {\n            return d1.isEqual(d2) || d1.isBefore(d2);\n        }\n\n        return d1.isBefore(d2);\n    }\n\n    /**\n     * Determines if {@code LocalDate} d1 occurs after {@code LocalDate} d2 and before {@code LocalDate} d3.\n     * The specified dates are inclusive.\n     *\n     * @param d1 {@code LocalDate} 1\n     * @param d2    {@code LocalDate} 2\n     * @param d3    {@code LocalDate} 3\n     * @return {@code true} if d1 occurs between d2 and d3\n     */\n    public static boolean between(final LocalDate d1, final LocalDate d2, final LocalDate d3) {\n        return after(d1, d2) && before(d1, d3);\n    }\n\n    /**\n     * Returns the number of days in the year.\n     *\n     * @param year calendar year\n     * @return the number of days in the year\n     */\n    public static int getDaysInYear(final int year) {\n        return LocalDate.ofYearDay(year, 1).lengthOfYear();\n    }\n\n    /**\n     * Returns an array of the first days bi-weekly for a given year.\n     *\n     * @param year The year to generate the array for\n     * @return The array of dates\n     */\n    public static LocalDate[] getFirstDayBiWeekly(final int year) {\n        return getFirstDayWeekly(Month.JANUARY, year,\n                (int) Math.ceil((float) DateUtils.getNumberOfWeeksInYear(year) / 2.0), 2);\n    }\n\n    /**\n     * Returns an array of the first days bi-weekly given a stating month, year, and the number of weeks\n     *\n     * @param statingMonth The Month to begin with\n     * @param year         The year to generate the array for\n     * @param weeks        The number of dates to return\n     * @see #getFirstDayWeekly(Month, int, int)\n     */\n    public static LocalDate[] getFirstDayBiWeekly(final Month statingMonth, final int year, final int weeks) {\n        return getFirstDayWeekly(statingMonth, year, weeks, 2);\n    }\n\n    /**\n     * Generates an array of dates starting on the first day of every month in\n     * the specified year.\n     *\n     * @param year The year to generate the array for\n     * @return The array of dates\n     */\n    public static LocalDate[] getFirstDayMonthly(final int year) {\n        return getFirstDayMonthly(Month.JANUARY, year, MONTHS_PER_YEAR);\n    }\n\n    /**\n     * Generates an array of dates starting on the first day of every month in\n     * the specified year.\n     *\n     * @param month  That month to start with\n     * @param year   The year to generate the array for\n     * @param months The number of months\n     * @return The array of dates\n     */\n    public static LocalDate[] getFirstDayMonthly(final Month month, final int year, final int months) {\n        return getFirstDayMonthly(month, year, months, 1);\n    }\n\n    /**\n     * Generates an array of dates starting on the first day of every month in\n     * the specified year.\n     *\n     * @param month  That month to start with\n     * @param year   The year to generate the array for\n     * @param months The number of months\n     * @param step   increment\n     * @return The array of dates\n     */\n   private static LocalDate[] getFirstDayMonthly(final Month month, final int year, final int months, final int step) {\n        LocalDate[] list = new LocalDate[months];\n\n        int index = 0;\n        int _month = month.getValue();\n        int _year = year;\n\n        while (index < months) {\n            list[index] = getFirstDayOfTheMonth(_month, _year);\n            _month += step;\n\n            if (_month > MONTHS_PER_YEAR) {\n                _year++;\n                _month = _month - MONTHS_PER_YEAR;\n            }\n\n            index++;\n        }\n        return list;\n    }\n\n    /**\n     * Returns a leveled date representing the first day of the month based on a\n     * specified date.\n     *\n     * @param date the base date to work from\n     * @return The last day of the month and year specified\n     */\n    public static LocalDate getFirstDayOfTheMonth(final LocalDate date) {\n        return date.with(TemporalAdjusters.firstDayOfMonth());\n    }\n\n    /**\n     * Returns a date representing the first day of the month.\n     *\n     * @param month The month (index starts at 1)\n     * @param year  The year (index starts at 1)\n     * @return The last day of the month and year specified\n     */\n    private static LocalDate getFirstDayOfTheMonth(final int month, final int year) {\n        return LocalDate.of(year, month, 1);\n    }\n\n    /**\n     * Returns an array of the starting date of each quarter in a year.\n     *\n     * @param year The year to generate the array for\n     * @return The array of quarter bound dates\n     */\n    public static LocalDate[] getFirstDayQuarterly(final int year) {\n        return getFirstDayMonthly(Month.JANUARY, year, 4, 3);\n    }\n\n    /**\n     * Returns an array of the starting date of each quarter in a year.\n     *\n     * @param year The year to generate the array for\n     * @return The array of quarter bound dates\n     */\n    public static LocalDate[] getFirstDayQuarterly(final Month statingMonth, final int year, final int weeks) {\n        return getFirstDayMonthly(statingMonth, year, weeks, 3);\n    }\n\n    /**\n     * Returns an array of Dates starting with the first day of each week of the\n     * year per ISO 8601.\n     *\n     * @param year The year to generate the array for\n     * @return The array of dates\n     * @see <a href=\"http://en.wikipedia.org/wiki/ISO_8601\">ISO_8601</a>\n     */\n    public static LocalDate[] getFirstDayWeekly(final int year) {\n        return getFirstDayWeekly(Month.JANUARY, year, getNumberOfWeeksInYear(year), 1);\n    }\n\n    /**\n     * Returns an array of weekly Dates per ISO 8601.\n     *\n     * @param statingMonth The Month to begin with\n     * @param year         The year to generate the array for\n     * @param weeks        The number of dates to return\n     * @return The array of dates\n     * @see <a href=\"http://en.wikipedia.org/wiki/ISO_8601\">ISO_8601</a>\n     */\n    public static LocalDate[] getFirstDayWeekly(final Month statingMonth, final int year, final int weeks) {\n        return getFirstDayWeekly(statingMonth, year, weeks, 1);\n    }\n\n    /**\n     * Returns an array of weekly Dates per ISO 8601.\n     *\n     * @param statingMonth The Month to begin with\n     * @param year         The year to generate the array for\n     * @param weeks        The number of dates to return\n     * @param step         The week increment\n     * @return The array of dates\n     * @see <a href=\"http://en.wikipedia.org/wiki/ISO_8601\">ISO_8601</a>\n     */\n    public static LocalDate[] getFirstDayWeekly(final Month statingMonth, final int year, final int weeks, final int step) {\n\n        LocalDate date = LocalDate.of(year, statingMonth, 1);\n\n        // Weeks begin on Monday per ISO_8601\n        date = date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));\n\n        // shift the stating date per ISO_8601 rules\n        switch (LocalDate.of(year, Month.JANUARY, 1).getDayOfWeek()) {\n            case FRIDAY:\n            case SATURDAY:\n            case SUNDAY:\n                break;\n            default:\n                date = date.minusDays(DAYS_PER_WEEK);\n\n                break;\n        }\n\n        final List<LocalDate> dates = new ArrayList<>();\n\n        dates.add(date);\n\n        for (int i = 1; i < weeks; i++) {\n            date = date.plusDays((long) DAYS_PER_WEEK * step);\n            dates.add(date);\n        }\n\n        return dates.toArray(new LocalDate[0]);\n    }\n\n    /**\n     * Returns the number of weeks in a year per ISO 8601.\n     *\n     * @param year year\n     * @return number of weeks\n     * @see <a href=\"http://en.wikipedia.org/wiki/ISO_8601\">ISO_8601</a>\n     */\n    public static int getNumberOfWeeksInYear(final int year) {\n        LocalDate midYear = LocalDate.of(year, Month.JUNE, 1);\n        return (int) midYear.range(WeekFields.ISO.weekOfWeekBasedYear()).getMaximum();\n    }\n\n    /**\n     * Returns an array of every day in a given year.\n     *\n     * @param year The year to generate the array for\n     * @return The array of dates\n     */\n    public static LocalDate[] getAllDays(final int year) {\n        return getAllDays(Month.JANUARY, year, getDaysInYear(year));\n    }\n\n    public static LocalDate[] getAllDays(final Month statingMonth, final int year, final int days) {\n        final List<LocalDate> dates = new ArrayList<>();\n\n        LocalDate start = LocalDate.of(year, statingMonth, 1);\n\n        dates.add(start);\n\n        for (int i = 1; i < days; i++) {\n            start = start.plusDays(1);\n            dates.add(start);\n        }\n\n        return dates.toArray(new LocalDate[0]);\n    }\n\n    /**\n     * Returns date representing the last day of the month given a specified date.\n     *\n     * @param date the base date to work from\n     * @return The last day of the month of the supplied date\n     */\n    public static LocalDate getLastDayOfTheMonth(@NotNull final LocalDate date) {\n        Objects.requireNonNull(date);\n\n        return date.with(TemporalAdjusters.lastDayOfMonth());\n    }\n\n    /**\n     * Generates an array of dates ending on the last day of every month between\n     * the start and stop dates.\n     *\n     * @param startDate The date to start at\n     * @param endDate   The data to stop at\n     * @return The array of dates\n     */\n    public static List<LocalDate> getLastDayOfTheMonths(final LocalDate startDate, final LocalDate endDate) {\n        final ArrayList<LocalDate> list = new ArrayList<>();\n\n        final LocalDate end = DateUtils.getLastDayOfTheMonth(endDate);\n        LocalDate t = DateUtils.getLastDayOfTheMonth(startDate);\n\n        /*\n         * add a month at a time to the previous date until all of the months\n         * have been captured\n         */\n        while (before(t, end)) {\n            list.add(t);\n\n            t = t.plusMonths(1);\n            t = t.with(TemporalAdjusters.lastDayOfMonth());\n        }\n        return list;\n    }\n\n    /**\n     * Generates an array of dates starting on the first day of every month\n     * between the start and stop dates.\n     *\n     * @param startDate The date to start at\n     * @param endDate   The data to stop at\n     * @return The array of dates\n     */\n    public static List<LocalDate> getFirstDayOfTheMonths(final LocalDate startDate, final LocalDate endDate) {\n        final ArrayList<LocalDate> list = new ArrayList<>();\n\n        final LocalDate end = DateUtils.getFirstDayOfTheMonth(endDate);\n        LocalDate t = DateUtils.getFirstDayOfTheMonth(startDate);\n\n        /*\n         * add a month at a time to the previous date until all of the months\n         * have been captured\n         */\n        while (before(t, end)) {\n            list.add(t);\n            t = t.with(TemporalAdjusters.firstDayOfNextMonth());\n        }\n        return list;\n    }\n\n    /**\n     * Returns a {@code LocalDate} date representing the last day of the quarter based on\n     * a specified date.\n     *\n     * @param date the base date to work from\n     * @return The last day of the quarter specified\n     */\n    public static LocalDate getLastDayOfTheQuarter(final LocalDate date) {\n        Objects.requireNonNull(date);\n\n        LocalDate result;\n\n        LocalDate[] bounds = getQuarterBounds(date);\n\n        if (date.compareTo(bounds[2]) < 0) {\n            result = bounds[1];\n        } else if (date.compareTo(bounds[4]) < 0) {\n            result = bounds[3];\n        } else if (date.compareTo(bounds[6]) < 0) {\n            result = bounds[5];\n        } else {\n            result = bounds[DAYS_PER_WEEK];\n        }\n\n        return result;\n    }\n\n    /**\n     * Returns a {@code LocalDate} representing the last day of the year based on a\n     * specified date.\n     *\n     * @param date the base date to work from\n     * @return The last day of the year specified\n     */\n    public static LocalDate getLastDayOfTheYear(@NotNull final LocalDate date) {\n        Objects.requireNonNull(date);\n\n        return date.with(TemporalAdjusters.lastDayOfYear());\n    }\n\n    /**\n     * Returns an array of quarter bound dates of the year based on a specified\n     * date. The order is q1s, q1e, q2s, q2e, q3s, q3e, q4s, q4e.\n     *\n     * @param date the base date to work from\n     * @return The array of quarter bound dates\n     */\n    private static LocalDate[] getQuarterBounds(final LocalDate date) {\n        Objects.requireNonNull(date);\n\n        final LocalDate[] bounds = new LocalDate[8];\n\n        bounds[0] = date.with(TemporalAdjusters.firstDayOfYear());\n        bounds[1] = date.withMonth(Month.MARCH.getValue()).with(TemporalAdjusters.lastDayOfMonth());\n        bounds[2] = date.withMonth(Month.APRIL.getValue()).with(TemporalAdjusters.firstDayOfMonth());\n        bounds[3] = date.withMonth(Month.JUNE.getValue()).with(TemporalAdjusters.lastDayOfMonth());\n        bounds[4] = date.withMonth(Month.JULY.getValue()).with(TemporalAdjusters.firstDayOfMonth());\n        bounds[5] = date.withMonth(Month.SEPTEMBER.getValue()).with(TemporalAdjusters.lastDayOfMonth());\n        bounds[6] = date.withMonth(Month.OCTOBER.getValue()).with(TemporalAdjusters.firstDayOfMonth());\n        bounds[DAYS_PER_WEEK] = date.with(TemporalAdjusters.lastDayOfYear());\n\n        return bounds;\n    }\n\n    /**\n     * Returns the number of the quarter (i.e. 1, 2, 3 or 4) based on a\n     * specified date.\n     *\n     * @param date the base date to work from\n     * @return The number of the quarter specified\n     */\n    public static int getQuarterNumber(final LocalDate date) {\n        Objects.requireNonNull(date);\n\n        int result;\n\n        LocalDate[] bounds = getQuarterBounds(date);\n\n        if (date.compareTo(bounds[2]) < 0) {\n            result = 1;\n        } else if (date.compareTo(bounds[4]) < 0) {\n            result = 2;\n        } else if (date.compareTo(bounds[6]) < 0) {\n            result = 3;\n        } else {\n            result = 4;\n        }\n\n        return result;\n    }\n\n    public static DateTimeFormatter getShortDateFormatter() {\n        return shortDateFormatter;\n    }\n\n    public static DateTimeFormatter getShortDateTimeFormatter() {\n        return shortDateTimeFormatter;\n    }\n\n    public static DateTimeFormatter getShortDateManualEntryFormatter() {\n        return shortDateManualEntryFormatter;\n    }\n\n    /**\n     * Returns the numerical week of the year given a date per the ISO 8601 standard.\n     *\n     * @param dateOfYear the base date to work from\n     * @return the week of the year\n     */\n    public static int getWeekOfTheYear(final LocalDate dateOfYear) {\n        return dateOfYear.get(WeekFields.ISO.weekOfWeekBasedYear());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/time/Period.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.time;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Period Enum.\n * \n * @author Craig Cavanaugh\n */\npublic enum Period {\n\n    DAILY(ResourceUtils.getString(\"Period.Daily\"), 1/30f),      // approximation\n    WEEKLY(ResourceUtils.getString(\"Period.Weekly\"), .25f),     // approximation\n    BI_WEEKLY(ResourceUtils.getString(\"Period.BiWeekly\"), .5f), // approximation\n    MONTHLY(ResourceUtils.getString(\"Period.Monthly\"), 1),\n    QUARTERLY(ResourceUtils.getString(\"Period.Quarterly\"), 3),\n    YEARLY(ResourceUtils.getString(\"Period.Yearly\" ), 12);\n\n    private final transient String description;\n\n    /**\n     * The number of months in a period.  The value may be an approximation\n     */\n    private final transient float months;\n\n    Period(final String description, final float months) {\n        this.description = description;\n        this.months = months;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n    public float getMonths() {\n        return months;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/CollectionUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Utility class for working with Collections.\n * \n * @author Craig Cavanaugh\n */\npublic class CollectionUtils {\n    \n    private CollectionUtils() {}\n\n    /**\n     * Sorts a map by it's value.\n     *\n     * @param map Map to sort\n     * @param <K> key\n     * @param <V> value\n     * @return sorted Map\n     */\n    public static <K, V extends Comparable<? super V>> Map<K, V> sortMapByValue(final Map<K, V> map) {\n        final List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());\n\n        list.sort(Map.Entry.comparingByValue());\n\n        final Map<K, V> sortedMap = new LinkedHashMap<>();\n\n        for (final Map.Entry<K, V> entry : list) {\n            sortedMap.put(entry.getKey(), entry.getValue());\n        }\n\n        return sortedMap;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/DefaultDaemonThreadFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * The default thread factory to be used for {@code ExecutorService} that\n * forces threads to be daemons\n * <p>\n * Example usage:\n * {@code\n * ExecutorService pool = Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory());\n * }\n *\n * @author Craig Cavanaugh\n *\n * @see java.util.concurrent.ExecutorService\n */\npublic class DefaultDaemonThreadFactory implements ThreadFactory {\n\n    private static final AtomicInteger poolNumber = new AtomicInteger(1);\n\n    private final AtomicInteger threadNumber = new AtomicInteger(1);\n\n    private final String namePrefix;\n\n    public DefaultDaemonThreadFactory(final String description) {\n        namePrefix = description + \"(\" + poolNumber.incrementAndGet() + \"), Thread \";\n    }\n\n    @Override\n    public Thread newThread(final @NotNull Runnable r) {\n        Thread t = new Thread(r, namePrefix + threadNumber.incrementAndGet());\n\n        t.setDaemon(true);\n        t.setPriority(Thread.NORM_PRIORITY);\n\n        return t;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/EncodeDecode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Locale;\nimport java.util.regex.Pattern;\n\nimport static java.util.stream.Collectors.joining;\n\n/**\n * EncodeDecode is used to encode/decode various objects using a String.\n *\n * @author Craig Cavanaugh\n */\npublic class EncodeDecode {\n\n    public static final Pattern COMMA_DELIMITER_PATTERN = Pattern.compile(\",\");\n\n    private static final char COMMA_DELIMITER = ',';\n\n    private EncodeDecode() {\n    }\n\n    /**\n     * Encodes a double array as a comma separated {@code String}. Values will be rounded to 2 decimal places.\n     * <p>\n     * The format is forced to use '.' as the decimal separator because some locales will use a comma.\n     *\n     * @param doubleArray array of doubles to encode\n     * @return resultant {@code String}\n     */\n    public static String encodeDoubleArray(final double[] doubleArray) {\n        final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);\n\n        final DecimalFormat df = (DecimalFormat) numberFormat;\n        df.applyPattern(\"#.##\");\n\n        return Arrays.stream(doubleArray).mapToObj(df::format).collect(joining(String.valueOf(COMMA_DELIMITER)));\n    }\n\n    /**\n     * Decodes a comma separated list of {@code double} into a primitive {@code double} array.\n     *\n     * @param string Comma separated string\n     * @return primitive {@code double} array\n     */\n    public static double[] decodeDoubleArray(@NotNull final String string) {\n        return Arrays.stream(COMMA_DELIMITER_PATTERN.split(string)).mapToDouble(Double::parseDouble).toArray();\n    }\n\n    /**\n     * Encodes a boolean array as a string of 1's and 0's.\n     *\n     * @param array a boolean array to encode as a String\n     * @return A string of 1's and 0's representing the boolean array\n     */\n    public static String encodeBooleanArray(final boolean[] array) {\n        StringBuilder buf = new StringBuilder();\n        if (array != null) {\n            for (boolean anArray : array) {\n                if (anArray) {\n                    buf.append('1');\n                } else {\n                    buf.append('0');\n                }\n            }\n            return buf.toString();\n        }\n        return null;\n    }\n\n    /**\n     * Turns a string of \"10101\" into a boolean array.\n     *\n     * @param array array to decode\n     * @return the boolean array, zero length if string is null or zero length\n     */\n    public static boolean[] decodeBooleanArray(final String array) {\n        if (array != null) {\n            int len = array.length();\n            if (len > 0) {\n                boolean[] b = new boolean[len];\n                for (int i = 0; i < len; i++) {\n                    if (array.charAt(i) == '1') {\n                        b[i] = true;\n                    }\n                }\n                return b;\n            }\n        }\n        return new boolean[0];\n    }\n\n    public static String encodeStringCollection(final Collection<String> list) {\n        return encodeStringCollection(list, COMMA_DELIMITER);\n    }\n\n    public static String encodeStringCollection(final Collection<String> list, final char delimiter) {\n\n        // precondition check for the delimiter existence\n        for (final String string : list) {\n            if (string.indexOf(delimiter) > -1) {\n                throw new RuntimeException(\"The list of strings may not contain a \" + delimiter);\n            }\n        }\n\n        return list.stream().collect(joining(String.valueOf(delimiter)));\n    }\n\n    public static Collection<String> decodeStringCollection(final String string) {\n        return decodeStringCollection(string, COMMA_DELIMITER);\n    }\n\n    public static Collection<String> decodeStringCollection(final String string, final char delimiter) {\n        if (string == null || string.isEmpty()) {\n            return Collections.emptyList();\n        }\n\n        Pattern pattern = Pattern.compile(String.valueOf(delimiter));\n\n        return java.util.Arrays.asList(pattern.split(string));\n    }\n\n    /**\n     * Converts a hex based color into an integer for compact storage\n     * @param color # based hex color\n     * @return int value\n     */\n    public static long colorStringToLong(final String color) {\n        return Long.parseUnsignedLong(color.replaceAll(\"0x\",\"\"), 16);\n    }\n\n    /**\n     * Converts a int based color into a hex format used by UI classes\n     * @param color int value\n     * @return hex based color string\n     */\n    public static String longToColorString(final long color) {\n        return String.format(\"0x%08X\", color);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/EncryptionManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.util;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.Key;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Base64;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * A Simple encryption class based on a supplied user and password.\n *\n * @author Craig Cavanaugh\n */\npublic class EncryptionManager {\n\n    private final Key key;\n\n    //public static final String ENCRYPTION_FLAG = \"encrypt\";\n\n    private static final String ENCRYPTION_ALGORITHM = \"AES\";\n\n    public static final String DECRYPTION_ERROR_TAG = \"<DecryptError>\";\n\n    private static final Logger logger = Logger.getLogger(EncryptionManager.class.getName());\n\n    public EncryptionManager(final char[] password) {\n        byte[] encryptionKey = \"fake\".getBytes(StandardCharsets.UTF_8);\n\n        try {\n            final MessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\n            final StringBuilder builder = new StringBuilder(new String(password));\n\n            // generate the encryption key\n            encryptionKey = md.digest(builder.toString().getBytes(StandardCharsets.UTF_8));\n\n            builder.delete(0, builder.length() - 1);\n        } catch (final NoSuchAlgorithmException e) {\n            LogUtil.logSevere(EncryptionManager.class, e);\n        }\n\n        key = new SecretKeySpec(encryptionKey, ENCRYPTION_ALGORITHM);\n    }\n\n    /**\n     * Encrypts the supplied string.\n     *\n     * @param plain String to encrypt\n     * @return the encrypted string\n     */\n    public String encrypt(final String plain) {\n\n        try {\n            final Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);\n\n            cipher.init(Cipher.ENCRYPT_MODE, key);\n\n            return Base64.getEncoder().encodeToString(cipher.doFinal(plain.getBytes(StandardCharsets.UTF_8)));\n\n        } catch (final InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException\n                | IllegalBlockSizeException e) {\n            LogUtil.logSevere(EncryptionManager.class, e);\n        }\n\n        return null;\n    }\n\n    /**\n     * Decrypts the supplied string.\n     *\n     * @param encrypted String to decrypt\n     * @return The decrypted string of {@code DECRYPTION_ERROR_TAG} if decryption fails\n     * @see #DECRYPTION_ERROR_TAG\n     */\n    public String decrypt(final String encrypted) {\n\n        try {\n            final Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);\n\n            cipher.init(Cipher.DECRYPT_MODE, key);\n\n            return new String(cipher.doFinal(Base64.getDecoder().decode(encrypted)), StandardCharsets.UTF_8);\n        } catch (final InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException\n                | IllegalBlockSizeException e) {\n            logger.log(Level.SEVERE, \"Invalid password\");\n            return DECRYPTION_ERROR_TAG;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/FileLocker.java",
    "content": "package jgnash.util;\n\nimport java.io.IOException;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.FileLock;\nimport java.nio.channels.OverlappingFileLockException;\nimport java.nio.file.Path;\nimport java.nio.file.StandardOpenOption;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class to encapsulate file locking\n *\n * @author Craig Cavanaugh\n */\npublic class FileLocker {\n    private FileLock fileLock = null;\n    private FileChannel lockChannel = null;\n\n    public boolean acquireLock(final Path path) {\n        boolean result = false;\n\n        try {\n            lockChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE);\n            fileLock = lockChannel.tryLock();\n\n            result = fileLock.isValid();\n        } catch (final IOException | OverlappingFileLockException ex) {\n            Logger.getLogger(FileLock .class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n        }\n\n        return result;\n    }\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    public boolean release() {\n        boolean result = false;\n\n        try {\n            if (fileLock != null) {\n                fileLock.release();\n                fileLock = null;\n            }\n\n            if (lockChannel != null) {\n                lockChannel.close();\n                lockChannel = null;\n            }\n\n            result = true;\n        } catch (final IOException ex) {\n            Logger.getLogger(FileLock .class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/FileMagic.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SeekableByteChannel;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.CharsetDecoder;\nimport java.nio.charset.MalformedInputException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamConstants;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamReader;\n\nimport jgnash.engine.Engine;\n\nimport static java.nio.charset.StandardCharsets.ISO_8859_1;\nimport static java.nio.charset.StandardCharsets.US_ASCII;\nimport static java.nio.charset.StandardCharsets.UTF_16;\nimport static java.nio.charset.StandardCharsets.UTF_16BE;\nimport static java.nio.charset.StandardCharsets.UTF_16LE;\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.nio.file.StandardOpenOption.READ;\n\n/**\n * Class to identify file types.\n *\n * @author Craig Cavanaugh\n */\npublic class FileMagic {\n\n    private static final Pattern COLON_DELIMITER_PATTERN = Pattern.compile(\":\");\n\n    private static final byte[] BINARY_XSTREAM_HEADER = new byte[]{10, -127, 0, 13, 111, 98, 106, 101, 99, 116, 45,\n            115, 116, 114, 101, 97, 109, 11, -127, 10};\n\n    private static final byte[] H2_HEADER = new byte[]{0x2D, 0x2D, 0x20, 0x48, 0x32, 0x20, 0x30, 0x2E, 0x35, 0x2F,\n            0x42, 0x20, 0x2D, 0x2D};\n\n    private static final byte[] H2MV_HEADER = new byte[]{72, 58, 50};\n\n    private static final byte[] HSQL_HEADER = \"SET DATABASE UNIQUE NAME HSQLDB\".getBytes(StandardCharsets.UTF_8);\n\n    private static final byte[] XML_HEADER = \"<?xml version=\\\"1.0\\\"\".getBytes(StandardCharsets.UTF_8);\n\n    private static final String USASCII = \"USASCII\";\n\n    private static final String WINDOWS_1252 = \"windows-1252\";\n\n    private static final Charset[] CHARSETS = {UTF_8, US_ASCII, ISO_8859_1, UTF_16, UTF_16BE, UTF_16LE};\n\n    private static final int BUFFER_SIZE = 8192;\n\n    private FileMagic() {\n    }\n\n    /**\n     * Returns the path type.\n     *\n     * @param path path to identify\n     * @return identified path type\n     */\n    public static FileType magic(final Path path) {\n\n        if (isValidVersion2File(path)) {\n            return FileType.jGnash2XML;\n        } else if (isBinaryXStreamFile(path)) {\n            return FileType.BinaryXStream;\n        } else if (isH2File(path)) {\n            return FileType.h2;\n        } else if (isH2MvFile(path)) {\n            return FileType.h2mv;\n        } else if (isHsqlFile(path)) {\n            return FileType.hsql;\n        } else if (isOfxV1(path)) {\n            return FileType.OfxV1;\n        } else if (isOfxV2(path)) {\n            return FileType.OfxV2;\n        }\n\n        return FileType.unknown;\n    }\n\n    /**\n     * Determine the correct character encoding of an OFX Version 1 file.\n     *\n     * @param path File to look at\n     * @return encoding of the file\n     */\n    public static String getOfxV1Encoding(final Path path) {\n\n        // Work through common standard Charsets\n        try {\n            return getOfxV1Encoding(path, StandardCharsets.UTF_8);\n        } catch (final MalformedInputException e) {\n            try {\n                return getOfxV1Encoding(path, ISO_8859_1);\n            } catch (final MalformedInputException ex) {\n                try {\n                    return getOfxV1Encoding(path, US_ASCII);\n                } catch (final MalformedInputException exx) {\n                    return WINDOWS_1252;\n                }\n            }\n        }\n    }\n\n    /**\n     * Determine the correct character encoding of an OFX Version 1 file.\n     *\n     * @param path File to look at\n     * @return encoding of the file\n     */\n    private static String getOfxV1Encoding(final Path path, final Charset charset) throws MalformedInputException {\n        String encoding = null;\n        String characterSet = null;\n\n        if (Files.exists(path)) {\n            try (final BufferedReader reader = Files.newBufferedReader(path, charset)) {\n                String line = reader.readLine();\n\n                while (line != null) {\n                    line = line.trim();\n\n                    if (!line.isEmpty()) { // allow empty lines at the beginning of the file\n                        if (line.startsWith(\"ENCODING:\")) {\n                            String[] splits = COLON_DELIMITER_PATTERN.split(line);\n\n                            if (splits.length == 2) {\n                                encoding = splits[1];\n                            }\n                        } else if (line.startsWith(\"CHARSET:\")) {\n                            String[] splits = COLON_DELIMITER_PATTERN.split(line);\n\n                            if (splits.length == 2) {\n                                characterSet = splits[1];\n                            }\n                        }\n\n                        if (encoding != null && characterSet != null) {\n                            break;\n                        }\n                    }\n                    line = reader.readLine();\n                }\n            } catch (final MalformedInputException e) {\n                throw e;    // rethrow\n            } catch (final IOException e) {\n                LogUtil.logSevere(FileMagic.class, e);\n            }\n        }\n\n        if (encoding != null && characterSet != null) {\n            if (encoding.equals(StandardCharsets.UTF_8.name()) && characterSet.equals(\"CSUNICODE\")) {\n                return ISO_8859_1.name();\n            } else if (encoding.equals(StandardCharsets.UTF_8.name())) {\n                return StandardCharsets.UTF_8.name();\n            } else if (encoding.equals(USASCII) && characterSet.equals(\"1252\")) {\n                return WINDOWS_1252;\n            } else if (encoding.equals(USASCII) && characterSet.contains(\"8859-1\")) {\n                return \"ISO-8859-1\";\n            } else if (encoding.equals(USASCII) && characterSet.equals(\"NONE\")) {\n                return WINDOWS_1252;\n            }\n        }\n\n        return WINDOWS_1252;\n    }\n\n\n    public static boolean isOfxV1(final Path path) {\n\n        boolean result = false;\n\n        if (Files.exists(path)) {\n\n            final String encoding = getOfxV1Encoding(path);\n            final Charset charset = Objects.requireNonNull(Charset.forName(encoding));\n\n            try (final BufferedReader reader = Files.newBufferedReader(path, charset)) {\n                String line = reader.readLine();\n\n                while (line != null) {\n\n                    line = line.trim(); // time white space.  Some OFX files may contain ugly white space\n\n                    if (!line.isEmpty()) { // allow empty lines at the beginning of the file\n\n                        if (line.contains(\"OFXHEADER:\")) {\n                            result = true;\n                        }\n                        break;\n                    }\n                    line = reader.readLine();\n                }\n            } catch (final IOException e) {\n                LogUtil.logSevere(FileMagic.class, e);\n            }\n        }\n\n        return result;\n    }\n\n    public static boolean isOfxV2(final Path path) {\n\n        boolean result = false;\n\n        if (Files.exists(path)) {\n\n            try (final BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {\n\n                String line = reader.readLine();\n\n                /* Assume that the OFX file is one continuous string of information when searching for OFXHEADER */\n                while (line != null) {\n                    line = line.trim();\n\n                    // consume any processing instructions and check for ofx 2.0 hints\n                    if (!line.isEmpty() && line.startsWith(\"<?\")) {\n                        if (line.contains(\"OFXHEADER=\\\"200\\\"\")) {\n                            result = true;\n                            break;\n                        }\n                    } else if (!line.isEmpty()) { // allow empty lines at the beginning of the file\n                        if (line.startsWith(\"<OFX>\")) { //must be ofx\n                            result = true;\n                        }\n                        break;\n                    }\n\n                    line = reader.readLine();\n                }\n            } catch (final MalformedInputException mie) {\n                result = false; // caused by binary file\n                Logger.getLogger(FileMagic.class.getName()).info(\"Tried to read a binary file\");\n            } catch (IOException e) {\n                Logger.getLogger(FileMagic.class.getName()).log(Level.SEVERE, e.toString(), e);\n            }\n        }\n\n        return result;\n    }\n\n    static boolean isBinaryXStreamFile(final Path path) {\n        return isFile(path, BINARY_XSTREAM_HEADER);\n    }\n\n    private static boolean isH2File(final Path path) {\n        return isFile(path, H2_HEADER);\n    }\n\n    private static boolean isH2MvFile(final Path path) {\n        return isFile(path, H2MV_HEADER);\n    }\n\n    private static boolean isHsqlFile(final Path path) {\n        return isFile(path, HSQL_HEADER);\n    }\n\n    private static boolean isFile(final Path path, final byte[] header) {\n        boolean result = false;\n\n        if (Files.exists(path)) {\n            try (final SeekableByteChannel channel = Files.newByteChannel(path, EnumSet.of(READ))) {\n                if (channel.size() > 0) { // must not be a zero length file\n                    final ByteBuffer buff = ByteBuffer.allocate(header.length);\n\n                    channel.read(buff);\n                    result = Arrays.equals(buff.array(), header);\n                    buff.clear();\n                }\n            } catch (final IOException ex) {\n                Logger.getLogger(FileMagic.class.getName()).log(Level.SEVERE, null, ex);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Determines if this is a valid jGnash XML file.\n     * <p>\n     * This will fail if the file was created by a future version of the file with a greater major version.\n     *\n     * @param path path to verify*\n     * @return {@code true} if valid.\n     */\n    private static boolean isValidVersion2File(final Path path) {\n\n        if (!Files.exists(path) || !Files.isReadable(path) || !Files.isRegularFile(path)) {\n            return false;\n        }\n\n        boolean result = false;\n\n        if (isFile(path, XML_HEADER)) {\n            try {\n                result = (int) Math.floor(Float.parseFloat(getXMLVersion(path))) <= Engine.CURRENT_MAJOR_VERSION;\n            } catch (final NumberFormatException nfe) {\n                Logger.getLogger(FileMagic.class.getName()).info(\"Invalid version string\");\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Determines the version of the jGnash file.\n     *\n     * @param path {@code file to check}\n     * @return file version as a String\n     */\n    private static String getXMLVersion(final Path path) {\n        String version = \"\";\n\n        try (final InputStream input = Files.newInputStream(path)) {\n            XMLInputFactory inputFactory = XMLInputFactory.newInstance();\n\n            // Protect against external entity attacks\n            inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);\n            inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);\n\n            XMLStreamReader reader = inputFactory.createXMLStreamReader(input, StandardCharsets.UTF_8.name());\n\n            while (reader.hasNext()) {\n                int event = reader.next();\n\n                if (event == XMLStreamConstants.PROCESSING_INSTRUCTION) {\n                    String name = reader.getPITarget();\n                    String data = reader.getPIData();\n\n                    if (name.equals(\"fileFormat\")) {\n                        version = data;\n                        break;\n                    }\n                }\n            }\n\n            reader.close();\n        } catch (final IOException e) {\n            LogUtil.logSevere(FileMagic.class, e);\n        } catch (final XMLStreamException e) {\n            Logger.getLogger(FileMagic.class.getName()).log(Level.INFO, \"{0} was not a valid jGnash XML_HEADER file\",\n                    path.toString());\n        }\n\n        return version;\n    }\n\n    private static boolean detectCharset(final InputStream inputStream, final Charset charset) {\n        boolean identified;\n\n        try (final BufferedInputStream input = new BufferedInputStream(inputStream)) {\n            final CharsetDecoder decoder = charset.newDecoder();\n            final byte[] buffer = new byte[BUFFER_SIZE];\n\n            identified = false;\n\n            while ((input.read(buffer) != -1) && (!identified)) {\n                try {\n                    decoder.decode(ByteBuffer.wrap(buffer));\n                    identified = true;\n                } catch (final CharacterCodingException e) {\n                    return false;\n                }\n            }\n        } catch (final IOException e) {\n            return false;\n        }\n\n        return identified;\n    }\n\n    private static boolean detectCharset(final Path path, final Charset charset) {\n        try {\n            return detectCharset(Files.newInputStream(path), charset);\n        } catch (final IOException e) {\n            return false;\n        }\n    }\n\n    private static Charset detectCharset(final Path path) {\n        for (final Charset charset : CHARSETS) {\n            if (detectCharset(path, charset)) {\n                return charset;\n            }\n        }\n\n        return UTF_8;   // default\n    }\n\n    /**\n     * Determines the Charset of an input stream.\n     *\n     * The stream will be closed after detection\n     * @param inputStream stream to check\n     * @return detected Charset\n     */\n    public static Charset detectCharset(final InputStream inputStream) {\n        for (final Charset charset : CHARSETS) {\n            if (detectCharset(inputStream, charset)) {\n                return charset;\n            }\n        }\n\n        return UTF_8;   // default\n    }\n\n    /**\n     * Determines the Charset of an input file\n     * The stream will be closed after detection\n     * @param fileName file to check\n     * @return detected Charset\n     */\n    public static Charset detectCharset(final String fileName) {\n        Charset charset = StandardCharsets.UTF_8;\n\n        final Path path = Paths.get(fileName);\n\n        if (Files.exists(path)) {\n            charset = FileMagic.detectCharset(path);\n            Logger.getLogger(FileMagic.class.getName()).info(fileName + \" was encoded as \" + charset);\n        }\n\n        return charset;\n    }\n\n    public enum FileType {\n        BinaryXStream, OfxV1, OfxV2, jGnash2XML, h2, h2mv, hsql, unknown\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/FileUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.FileLock;\nimport java.nio.channels.OverlappingFileLockException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.StandardCopyOption;\nimport java.nio.file.StandardOpenOption;\nimport java.nio.file.StandardWatchEventKinds;\nimport java.nio.file.WatchEvent;\nimport java.nio.file.WatchKey;\nimport java.nio.file.WatchService;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport java.util.zip.Deflater;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport jgnash.engine.jpa.JpaH2DataStore;\nimport jgnash.engine.jpa.JpaH2MvDataStore;\nimport jgnash.engine.jpa.JpaHsqlDataStore;\nimport jgnash.engine.xstream.XMLDataStore;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * File utilities.\n *\n * @author Craig Cavanaugh\n */\npublic final class FileUtils {\n\n    /**\n     * Regular expression for returning the file extension.\n     */\n    private static final String FILE_EXT_REGEX = \"(?<=\\\\.).*$\";\n\n    private static final String[] FILE_LOCK_EXTENSIONS = new String[]{JpaHsqlDataStore.LOCK_EXT,\n            JpaH2DataStore.LOCK_EXT, JpaH2MvDataStore.LOCK_EXT, \".lock\"};\n\n    private static final String[] FILE_EXTENSIONS = new String[]{JpaH2DataStore.H2_FILE_EXT,\n            JpaH2MvDataStore.MV_FILE_EXT, JpaHsqlDataStore.FILE_EXT, XMLDataStore.FILE_EXT};\n\n    public static final String SEPARATOR = System.getProperty(\"file.separator\");\n\n    private static final int DEFAULT_BUFFER_SIZE = 8192;\n\n    // HSQLDB ~10.10 seconds\n    // H2 database ~2.0 seconds\n    private static final long LOCK_FILE_PERIOD = 11000;\n\n    private static final int LOCK_WAIT_PERIOD = 20;\n\n    private FileUtils() {\n    }\n\n    /**\n     * Deletes a path and it's contents.\n     *\n     * @param path {@code Path} to delete\n     * @throws IOException thrown if an IO error occurs\n     */\n    public static void deletePathAndContents(final Path path) throws IOException {\n        if (Files.exists(path)) {   // only try if it exists\n\n            Files.walkFileTree(path, new SimpleFileVisitor<>() {\n                @Override\n                public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {\n                    Files.delete(file);\n                    return FileVisitResult.CONTINUE;\n                }\n\n                @Override\n                public FileVisitResult postVisitDirectory(final Path dir, final IOException ex) throws IOException {\n                    Files.delete(dir);\n                    return FileVisitResult.CONTINUE;\n                }\n            });\n        }\n    }\n\n    /**\n     * Determines if a file has been locked for use. A lock file check is performed\n     * at the filesystem level and the actual file is checked for a locked state at the OS level.\n     *\n     * @param fileName file name to check for locked state\n     * @return true if a lock file is found or the file is locked at the OS level.\n     * @throws java.io.IOException thrown if file does not exist or it is a directory\n     */\n    public static boolean isFileLocked(final String fileName) throws IOException {\n\n        boolean result = false;\n\n        if (Files.exists(Paths.get(fileName))) {    // false if the base file is not found\n            for (final String extension : FILE_LOCK_EXTENSIONS) {\n                if (Files.exists(Paths.get(FileUtils.stripFileExtension(fileName) + extension))) {\n                    result = true;\n                    break;\n                }\n            }\n\n            if (!result) {\n                try (final FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ,\n                        StandardOpenOption.WRITE)) {\n\n                    try (final FileLock lock = channel.tryLock()) {\n                        if (lock == null) {\n                            result = true;\n                        }\n                    } catch (final OverlappingFileLockException ex) {   // indicates file is already locked\n                        result = true;\n                    }\n                } catch (final IOException e) {\n                    logSevere(FileUtils.class, e);\n                    throw e;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Determines if a lock file is stale / leftover from a crash\n     *\n     * @param fileName database file/lockfile to test\n     * @return true if the lock file is determined to be stale\n     * @throws IOException thrown if file does not exist or it is a directory\n     */\n    public static boolean isLockFileStale(final String fileName) throws IOException {\n        boolean result = false;\n\n        final long maxIterations = LOCK_FILE_PERIOD / LOCK_WAIT_PERIOD;\n\n        search:\n        for (final String extension : FILE_LOCK_EXTENSIONS) {\n\n            final Path path = Paths.get(FileUtils.stripFileExtension(fileName) + extension);\n\n            if (Files.exists(path)) {\n\n                for (int i = 0; i < maxIterations; i++) {\n\n                    long timeStamp = lastModified(path);\n\n                    if (System.currentTimeMillis() - timeStamp > LOCK_FILE_PERIOD) {\n                        result = true;\n                        break search;\n                    }\n\n                    try {\n                        Thread.sleep(LOCK_WAIT_PERIOD);\n                    } catch (final InterruptedException e) {\n                        logSevere(FileUtils.class, e);\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        return result;\n    }\n\n    public static boolean deleteLockFile(final String fileName) throws IOException {\n        boolean result = false;\n\n        for (final String extension : FILE_LOCK_EXTENSIONS) {\n            final Path path = Paths.get(FileUtils.stripFileExtension(fileName) + extension);\n\n            if (Files.exists(path)) {\n                Files.delete(path);\n                result = true;\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Strips the file extensions off the supplied filename including the period.\n     * <p>\n     * Known @code(DataStore} extensions are checked first prior to assuming a simple file extension.\n     * If the supplied filename does not contain an extension ending with a period, the original is returned.\n     *\n     * @param fileName filename to strip the extension off\n     * @return filename with extension removed\n     */\n    public static String stripFileExtension(@NotNull final String fileName) {\n\n        // check for known types first\n        for (final String extension : FILE_EXTENSIONS) {\n            int index = fileName.toLowerCase().lastIndexOf(extension.toLowerCase());\n\n            if (index >= 0) {\n                return fileName.substring(0, index);\n            }\n        }\n\n        int index = fileName.lastIndexOf('.');\n\n        if (index >= 0) {\n            return fileName.substring(0, index);\n        }\n\n        return fileName;\n    }\n\n    /**\n     * Returns the modification time of a file\n     *\n     * @param path file to check\n     * @return last modification timestamp in millis\n     * @throws IOException thrown if file is not found\n     */\n    private static long lastModified(final Path path) throws IOException {\n        return Files.getLastModifiedTime(path).toMillis();\n    }\n\n    /**\n     * Determine if the supplied file name has an extension.\n     *\n     * @param fileName filename to check\n     * @return true if supplied file has an extension\n     */\n    public static boolean fileHasExtension(final String fileName) {\n        return !stripFileExtension(fileName).equals(fileName);\n    }\n\n    /**\n     * Returns the file extension if it has one\n     * jGnash specific file extensions are check first.\n     *\n     * @param fileName file name to extract extension from\n     * @return file extension or an empty string if not found.\n     */\n    public static String getFileExtension(final String fileName) {\n        Objects.requireNonNull(fileName);\n\n        for (final String extension : FILE_EXTENSIONS) {\n            if (fileName.toLowerCase().endsWith(extension.toLowerCase())) {\n                return extension.substring(1);  // skip the leading period\n            }\n        }\n\n        String result = \"\";\n\n        final Pattern pattern = Pattern.compile(FILE_EXT_REGEX);\n        final Matcher matcher = pattern.matcher(fileName);\n\n        if (matcher.find()) {\n            result = matcher.group();\n        }\n\n        return result;\n    }\n\n    /**\n     * Make a copy of a file given a source and destination.\n     *\n     * @param src Source file\n     * @param dst Destination file\n     * @return true if the copy was successful\n     */\n    public static boolean copyFile(final Path src, final Path dst) {\n\n        boolean result = false;\n\n        if (src != null && dst != null && !src.equals(dst)) {\n            try {\n                Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);\n                result = true;\n            } catch (final IOException e) {\n                logSevere(FileUtils.class, e);\n            }\n\n        }\n\n        return result;\n    }\n\n    public static void compressFile(@NotNull final Path source, @NotNull final Path destination) {\n\n        // Create the destination directory if needed\n        if (!Files.isDirectory(destination.getParent())) {\n            try {\n                Files.createDirectories(destination.getParent());\n                Logger.getLogger(FileUtils.class.getName()).info(\"Created directories\");\n            } catch (final IOException e) {\n                logSevere(FileUtils.class, e);\n            }\n        }\n\n        // Try to open the zip file for output\n        try (final OutputStream fos = new BufferedOutputStream(Files.newOutputStream(destination));\n             final ZipOutputStream zipOut = new ZipOutputStream(fos)) {\n\n            // Try to open the input stream\n            try (final InputStream in = new BufferedInputStream(Files.newInputStream(source, StandardOpenOption.READ))) {\n                zipOut.setLevel(Deflater.BEST_COMPRESSION);\n\n                // strip the path when creating the zip entry\n                zipOut.putNextEntry(new ZipEntry(source.getFileName().toString()));\n\n                // Transfer bytes from the file to the ZIP file\n                int length;\n\n\n                byte[] ioBuffer = new byte[DEFAULT_BUFFER_SIZE];\n\n                while ((length = in.read(ioBuffer)) > 0) {\n                    zipOut.write(ioBuffer, 0, length);\n                }\n\n                // finish the zip entry, but let the try-with-resources handle the close\n                zipOut.finish();\n            }\n        } catch (final IOException e) {\n            logSevere(FileUtils.class, e);\n        }\n    }\n\n    /**\n     * Returns a sorted list of files in a specified directory that match a regex search pattern.\n     *\n     * @param directory    base directory for the search\n     * @param regexPattern regex search pattern\n     * @return a List of matching Files. The list will be empty if no matches\n     * are found or if the directory is not valid.\n     */\n    public static List<Path> getDirectoryListing(final Path directory, final String regexPattern) {\n        final List<Path> fileList = new ArrayList<>();\n\n        if (directory != null && Files.isDirectory(directory)) {\n            final Pattern p = Pattern.compile(regexPattern);\n\n            try (final Stream<Path> stream = Files.list(directory)) {\n                fileList.addAll(stream.filter(path -> p.matcher(path.toString()).matches())\n                        .collect(Collectors.toList()));\n            } catch (IOException e) {\n                logSevere(FileUtils.class, e);\n            }\n\n            Collections.sort(fileList); // sort in natural order\n        }\n\n        return fileList;\n    }\n\n    /**\n     * Blocks for a specified period of time for removal of a file\n     *\n     * @param fileName file to monitor\n     * @param timeout  timeout in milliseconds\n     * @return true if the file existed and was removed\n     */\n    public static boolean waitForFileRemoval(final String fileName, final long timeout) {\n        boolean result = false;\n\n        final LocalDateTime start = LocalDateTime.now();\n\n        if (fileName != null && Files.exists(Paths.get(fileName))) {\n            final Path filePath = Paths.get(fileName);\n\n            try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {\n                filePath.getParent().register(watchService, StandardWatchEventKinds.ENTRY_DELETE);\n\n                while (Duration.between(start, LocalDateTime.now()).toMillis() < timeout) {\n                    final WatchKey watchKey = watchService.poll(timeout, TimeUnit.MILLISECONDS);\n\n                    if (watchKey != null) {\n                        for (final WatchEvent<?> ignored : watchKey.pollEvents()) {\n                            if (!Files.exists(filePath)) {\n                                Logger.getLogger(FileUtils.class.getName()).info(fileName + \" was removed\");\n                                result = true;\n                                break;\n                            }\n                        }\n                    }\n                }\n            } catch (final IOException e) {\n                logSevere(FileUtils.class, e);\n            } catch (final InterruptedException e) {\n                logSevere(FileUtils.class, e);\n                Thread.currentThread().interrupt();\n            }\n        } else {\n            result = true;  // lock file was already gone\n        }\n\n        // Last check for file existence.  File removal could have occurred before watch service started\n        if (!result && Files.exists(Paths.get(fileName))) {\n            Logger.getLogger(FileUtils.class.getName()).info(\"Timed out waiting for removal of: \" + fileName);\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/LocaleObject.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\n/**\n * Utility Class for display and ordering of {@code Locale} objects in a nice readable and sorted order.\n *\n * @author Craig Cavanaugh\n */\npublic class LocaleObject implements Comparable<LocaleObject> {\n\n    private final Locale locale;\n\n    private final String display;\n\n    public LocaleObject(final Locale locale) {\n        this.locale = Objects.requireNonNull(locale);\n\n        display = locale.getDisplayName() + \" - \" + locale + \"  [\" + locale.getDisplayName(locale) + \"]\";\n    }\n\n    public Locale getLocale() {\n        return locale;\n    }\n\n    @Override\n    public final String toString() {\n        return display;\n    }\n\n    @Override\n    public int compareTo(@NotNull final LocaleObject o) {\n        return toString().compareTo(o.toString());\n    }\n\n    @Override\n    public boolean equals(final Object obj) {\n        if (obj instanceof LocaleObject) {\n            return equals((LocaleObject) obj);\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(locale, display);\n    }\n\n    private boolean equals(final LocaleObject obj) {\n        return obj.locale.equals(locale);\n    }\n\n    public static Collection<LocaleObject> getLocaleObjects() {\n        return Arrays.stream(Locale.getAvailableLocales())\n            .filter(l -> l.getDisplayName() != null\n                && !l.getDisplayName().isEmpty()\n                && !l.getCountry().isEmpty())\n            .map(LocaleObject::new)\n            .sorted().collect(Collectors.toList());\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/LockedCommodityNode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport jgnash.engine.CommodityNode;\n\n/**\n * Decorator around a CommodityNode to indicate it is in a locked/unlocked state.\n *\n * @author Craig Cavanaugh\n */\npublic class LockedCommodityNode<T extends CommodityNode> implements Comparable<LockedCommodityNode<T>> {\n\n    private final boolean locked;\n    private final T t;\n\n    public LockedCommodityNode(@NotNull final T t, final boolean locked) {\n        this.t = t;\n        this.locked = locked;\n    }\n\n    @Override\n    public String toString() {\n        return t.toString();\n    }\n\n    public boolean isLocked() {\n        return locked;\n    }\n\n    @Override\n    public int compareTo(@NotNull final LockedCommodityNode<T> other) {\n        return t.compareTo(other.t);\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n\t@Override\n    public boolean equals(final Object o) {\n        return this == o || o instanceof LockedCommodityNode && t.equals(((LockedCommodityNode) o).t);\n    }\n\n    public T getNode() {\n        return t;\n    }\n\n    @Override\n    public int hashCode() {\n        int hash = t.hashCode();\n        return 13 * hash + (locked ? 1 : 0);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/LogUtil.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.logging.Level;\nimport java.util.logging.LogManager;\nimport java.util.logging.Logger;\n\n/**\n * Utility class to reduce logging code\n * <p>\n * A fair amount of byte code is generated to log a throwable.\n * <p>\n * This class provides a static method which produces much less byte code when used.\n *\n * @author Craig Cavanaugh\n */\npublic class LogUtil {\n\n    static {\n\n        try (final InputStream stream = LogUtil.class.getClassLoader()\n                .getResourceAsStream(\"logging.properties\")) {\n            LogManager.getLogManager().readConfiguration(stream);\n\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private LogUtil() {\n        // utility class\n    }\n\n    public static void configureLogging() {\n        Logger.getLogger(LogUtil.class.getName()).info(\"Logging configured\");\n    }\n\n    /**\n     * Logs a throwable at Level.SEVERE.\n     *\n     * @param clazz     calling class\n     * @param throwable Throwable to log\n     */\n    public static void logSevere(final Class<?> clazz, final Throwable throwable) {\n        Logger.getLogger(clazz.getName()).log(Level.SEVERE, throwable.getLocalizedMessage(), throwable);\n    }\n\n    /**\n     * Logs a throwable at Level.SEVERE.\n     *\n     * @param clazz   calling class\n     * @param message error message\n     */\n    public static void logSevere(final Class<?> clazz, final String message) {\n        Logger.getLogger(clazz.getName()).log(Level.SEVERE, message);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/MathEval.java",
    "content": "package jgnash.util;\n\n/**\n * A simple recursive decent math parser taken from public domain code:\n * <p>\n * https://stackoverflow.com/questions/3422673/how-to-evaluate-a-math-expression-given-in-string-form/26227947#26227947\n */\npublic class MathEval {\n\n    private final String str;\n\n    private int pos = -1;\n    private int ch;\n\n    private MathEval(final String str) {\n        this.str = str;\n    }\n\n    public static double eval(final String str) throws ArithmeticException {\n        return new MathEval(str).parse();\n    }\n\n    private void nextChar() {\n        ch = (++pos < str.length()) ? str.charAt(pos) : -1;\n    }\n\n    private boolean eat(final int charToEat) {\n        while (ch == ' ') {\n            nextChar();\n        }\n\n        if (ch == charToEat) {\n            nextChar();\n\n            return true;\n        }\n        return false;\n    }\n\n    private double parse() throws ArithmeticException {\n        nextChar();\n\n        double x = parseExpression();\n\n        if (pos < str.length()) {\n            throw new ArithmeticException(\"Unexpected: \" + (char) ch);\n        }\n        return x;\n    }\n\n    // Grammar:\n    // expression = term | expression `+` term | expression `-` term\n    // term = factor | term `*` factor | term `/` factor\n    // factor = `+` factor | `-` factor | `(` expression `)`\n    //        | number | functionName factor | factor `^` factor\n\n    private double parseExpression() {\n        double x = parseTerm();\n\n        for (; ; ) {\n            if (eat('+')) {\n                x += parseTerm(); // addition\n            } else if (eat('-')) {\n                x -= parseTerm(); // subtraction\n            } else {\n                return x;\n            }\n        }\n    }\n\n    private double parseTerm() {\n        double x = parseFactor();\n\n        for (; ; ) {\n            if (eat('*')) {\n                x *= parseFactor(); // multiplication\n            } else if (eat('/')) {\n                x /= parseFactor(); // division\n            } else {\n                return x;\n            }\n        }\n    }\n\n    private double parseFactor() {\n        if (eat('+')) { // unary plus\n            return parseFactor();\n        }\n\n        if (eat('-')) {  // unary minus\n            return -parseFactor();\n        }\n\n        double x;\n\n        int startPos = this.pos;\n\n        if (eat('(')) { // parentheses\n            x = parseExpression();\n            eat(')');\n        } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers\n            while ((ch >= '0' && ch <= '9') || ch == '.') {\n                nextChar();\n            }\n            try {\n                x = Double.parseDouble(str.substring(startPos, this.pos));\n            } catch (final NumberFormatException nfe) {\n                return Double.NaN;  // return NaN if the parser failed\n            }\n        } else {\n            throw new ArithmeticException(\"Unexpected: \" + (char) ch);\n        }\n\n        return x;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/MultiHashMap.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * A HashMap that allows multiple values for the same key.  If multiple\n * values exist for a given key, the values are treated as a FILO buffer.\n * <p>\n * This class is thread-safe\n *\n * @param <K> Key class\n * @param <V> Object class\n * @author Craig Cavanaugh\n */\npublic class MultiHashMap<K, V> extends HashMap<K, Object> {\n\n    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public Object put(final K key, final Object value) {\n        if (value instanceof ArrayList<?>) {\n            throw new IllegalArgumentException(\"ArrayLists are not allowed\");\n        }\n\n        lock.writeLock().lock();\n\n        try {\n            if (containsKey(key)) {\n                Object val = super.get(key);\n                if (val instanceof ArrayList<?>) {\n                    ArrayList<Object> l = (ArrayList<Object>) val;\n                    l.add(value);\n                    return l.get(l.size() - 2);\n                }\n                ArrayList<Object> l = new ArrayList<>();\n                l.add(val);\n                l.add(value);\n                super.put(key, l);\n                return val;\n            }\n            return super.put(key, value);\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * By default the last value in, is the first value out.\n     */\n    @SuppressWarnings(\"rawtypes\")\n    @Override\n    public Object get(final Object key) {\n        lock.readLock().lock();\n\n        try {\n            Object v = super.get(key);\n            if (v instanceof ArrayList<?>) {\n                ArrayList l = (ArrayList) v;\n                return l.get(l.size() - 1);\n            }\n            return v;\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    /**\n     * Returns all of the objects associated with a given key.  This is guaranteed to\n     * return a valid list.\n     *\n     * @param key The key to use\n     * @return The list of values associated with the key\n     */\n    @SuppressWarnings(\"unchecked\")\n    public List<V> getAll(final Object key) {\n        lock.readLock().lock();\n\n        try {\n            Object v = super.get(key);\n            if (v instanceof ArrayList) {\n                return Collections.unmodifiableList((ArrayList<V>) v);\n            } else if (v == null) {\n                return Collections.emptyList();\n            } else {\n                return Collections.singletonList((V) v);\n            }\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    @SuppressWarnings({\"unchecked\", \"rawtypes\", \"element-type-mismatch\"})\n    @Override\n    public Object remove(final Object key) {\n\n        lock.writeLock().lock();\n\n        try {\n            Object v = super.get(key);\n            if (v instanceof ArrayList) {\n                ArrayList l = (ArrayList) v;\n                if (l.size() == 2) {\n                    v = l.get(1);\n                    super.put((K) key, l.get(0)); // remove the ArrayList\n\n                    return v;\n                }\n                return l.remove(l.size() - 1);\n            }\n            return super.remove(key);\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Removes a specific value.\n     *\n     * @param key   The key of the value to remove\n     * @param value The specific value to remove\n     * @return {@code true} if the map changed\n     */\n    @SuppressWarnings(\"rawtypes\")\n    public boolean removeValue(final K key, final V value) {\n\n        boolean result = false;\n\n        lock.writeLock().lock();\n\n        try {\n            Object v = super.get(key);\n            if (v instanceof ArrayList) {\n                ArrayList l = (ArrayList) v;\n                if (l.remove(value)) {\n                    if (l.size() == 1) { // remove the ArrayList\n                        super.put(key, value);\n                    }\n                    result = true;\n                }\n            } else if (v != null && v.equals(value)) {\n                result = super.remove(key) != null;\n            }\n        } finally {\n            lock.writeLock().unlock();\n        }\n\n        return result;\n    }\n}"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/NewFileUtility.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.Collection;\nimport java.util.ResourceBundle;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountTreeXMLFactory;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.RootAccount;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Utility class for generating new files.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileUtility {\n\n    public static void buildNewFile(final String fileName, final DataStoreType dataStoreType, final char[] password,\n                                    final CurrencyNode currencyNode, final Collection<CurrencyNode> currencyNodes,\n                                    final Collection<RootAccount> rootAccountCollection) throws IOException {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        // have to close the engine first\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        // try to delete any existing database\n        if (Files.exists(Paths.get(fileName))) {\n            if (!EngineFactory.deleteDatabase(fileName)) {\n                throw new IOException(ResourceUtils.getString(\"Message.Error.DeleteExistingFile\", fileName));\n            }\n        }\n\n        // create the directory if needed\n        Files.createDirectories(Paths.get(fileName).getParent());\n\n        final Engine e = EngineFactory.bootLocalEngine(fileName, EngineFactory.DEFAULT, password, dataStoreType);\n\n        CurrencyNode defaultCurrency = currencyNode;\n\n        // match the existing default to the new default.  If they match, replace to prevent\n        // creation of a duplicate currency\n        if (e.getDefaultCurrency().matches(defaultCurrency)) {\n            defaultCurrency = e.getDefaultCurrency();\n        }\n\n        // make sure a duplicate default is not added\n        for (final CurrencyNode node : currencyNodes) {\n            if (!node.matches(defaultCurrency)) {\n                e.addCurrency(node);\n            }\n        }\n\n        if (!defaultCurrency.equals(e.getDefaultCurrency())) {\n            e.setDefaultCurrency(defaultCurrency);\n        }\n\n        if (!rootAccountCollection.isEmpty()) { // import account sets\n            for (final RootAccount root : rootAccountCollection) {\n                AccountTreeXMLFactory.importAccountTree(e, root);\n            }\n        } else { // none selected, create a very basic account set\n            final RootAccount root = e.getRootAccount();\n\n            final Account bank = new Account(AccountType.BANK, defaultCurrency);\n            bank.setDescription(resources.getString(\"Name.BankAccounts\"));\n            bank.setName(resources.getString(\"Name.BankAccounts\"));\n\n            e.addAccount(root, bank);\n\n            final Account income = new Account(AccountType.INCOME, defaultCurrency);\n            income.setDescription(resources.getString(\"Name.IncomeAccounts\"));\n            income.setName(resources.getString(\"Name.IncomeAccounts\"));\n\n            e.addAccount(root, income);\n\n            final Account expense = new Account(AccountType.EXPENSE, defaultCurrency);\n            expense.setDescription(resources.getString(\"Name.ExpenseAccounts\"));\n            expense.setName(resources.getString(\"Name.ExpenseAccounts\"));\n\n            e.addAccount(root, expense);\n        }\n    }\n\n    private NewFileUtility() {\n        // Utility class\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/NotNull.java",
    "content": "package jgnash.util;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Documented\n@Retention(RetentionPolicy.CLASS)\n@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\npublic @interface NotNull {\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/Nullable.java",
    "content": "package jgnash.util;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Documented\n@Retention(RetentionPolicy.CLASS)\n@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\npublic @interface Nullable {\n\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/SearchUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.util.regex.Pattern;\n\n/**\n * Search Utility class.\n * \n * @author Craig Cavanaugh\n */\npublic class SearchUtils {\n\n    private SearchUtils() {\n    }\n\n    /**\n     * Creates a Pattern given a DOS style wildcard search pattern.\n     * \n     * @param pattern search pattern\n     * @param caseSensitive true if the Pattern should be case sensitive\n     * @return Pattern according to specified parameters\n     */\n    public static Pattern createSearchPattern(final String pattern, final boolean caseSensitive) {\n        Pattern p;\n\n        if (caseSensitive) {\n            p = Pattern.compile(wildcardSearchToRegex(pattern));\n        } else {\n            p = Pattern.compile(wildcardSearchToRegex(pattern), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);\n        }\n\n        return p;\n    }\n\n    private static String wildcardSearchToRegex(final String wildcard) {\n        StringBuilder buffer = new StringBuilder(wildcard.length() + 2);\n        buffer.append('^');\n\n        for (char c : wildcard.toCharArray()) {\n\n            if (c == '*') {\n                buffer.append(\".*\");\n            } else if (c == '?') {\n                buffer.append('.');\n            } else if (\"+()^$.{}[]|\\\\\".indexOf(c) != -1) {\n                buffer.append('\\\\').append(c); // escape special regexp-characters\n            } else {\n                buffer.append(c);\n            }\n        }\n\n        buffer.append('$');\n        return buffer.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/function/MemoPredicate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.function;\n\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.function.Predicate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport jgnash.engine.Transaction;\nimport jgnash.util.SearchUtils;\n\n/**\n * Filter for Transaction memos.\n *\n * @author Craig Cavanaugh\n */\npublic class MemoPredicate implements Predicate<Transaction> {\n\n    private final String filter;\n\n    private final Pattern pattern;\n\n    public MemoPredicate(final String filter, final boolean useRegex) {\n        if (useRegex && !filter.isEmpty()) {\n            pattern = SearchUtils.createSearchPattern(Objects.requireNonNull(filter), false);\n            this.filter = null;\n        } else {\n            pattern = null;\n            this.filter = filter.toLowerCase(Locale.getDefault());\n        }\n    }\n\n    @Override\n    public boolean test(final Transaction transaction) {\n        if (pattern != null) {\n            final Matcher matcher = pattern.matcher(transaction.getMemo());\n            return matcher.matches();\n        } else if (filter != null && !filter.isEmpty()) {\n            return transaction.getMemo().toLowerCase(Locale.getDefault()).contains(filter);\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/function/ParentAccountPredicate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.function;\n\nimport java.util.function.Predicate;\n\nimport jgnash.engine.Account;\n\n/**\n * Predicate that only allows Accounts with children.\n *\n * @author Craig Cavanaugh\n */\npublic class ParentAccountPredicate implements Predicate<Account> {\n\n    @Override\n    public boolean test(final Account account) {\n       return account.isParent();\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/function/PayeePredicate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.function;\n\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.function.Predicate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport jgnash.engine.Transaction;\nimport jgnash.util.NotNull;\nimport jgnash.util.SearchUtils;\n\n/**\n * Filter for Transaction payees.\n *\n * @author Craig Cavanaugh\n */\npublic class PayeePredicate implements Predicate<Transaction> {\n\n    private final String filter;\n\n    private final Pattern pattern;\n\n    public PayeePredicate(@NotNull final String filter, final boolean useRegex) {\n        if (useRegex && !filter.isEmpty()) {\n            pattern = SearchUtils.createSearchPattern(Objects.requireNonNull(filter), false);\n            this.filter = null;\n        } else {\n            pattern = null;\n            this.filter = filter.toLowerCase(Locale.getDefault());\n        }\n    }\n\n    @Override\n    public boolean test(final Transaction transaction) {\n        if (pattern != null) {\n            final Matcher matcher = pattern.matcher(transaction.getPayee());\n            return matcher.matches();\n        } else if (filter != null && !filter.isEmpty()) {\n            return transaction.getPayee().toLowerCase(Locale.getDefault()).contains(filter);\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/function/ReconciledPredicate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.function;\n\nimport java.util.function.Predicate;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Predicate for the reconciled state of a transaction.\n *\n * @author Craig Cavanaugh\n */\npublic class ReconciledPredicate implements Predicate<Transaction> {\n\n    private final Account account;\n    private final ReconciledState reconciledState;\n\n    public ReconciledPredicate(@NotNull Account account, @Nullable final ReconciledState reconciledState) {\n        this.account = account;\n        this.reconciledState = reconciledState;\n    }\n\n    @Override\n    public boolean test(final Transaction transaction) {\n        if (reconciledState == null) {\n            return true;\n        }\n\t\treturn reconciledState == transaction.getReconciled(account);\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/function/TagPredicate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.function;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport jgnash.engine.Tag;\nimport jgnash.engine.Transaction;\nimport jgnash.util.NotNull;\n\n/**\n * Predicate for the Tags assigned to a transaction.\n *\n * @see jgnash.engine.Tag\n *\n * @author Craig Cavanaugh\n */\npublic class TagPredicate implements Predicate<Transaction> {\n\n    private final Set<Tag> tags = new HashSet<>();\n\n    public TagPredicate(@NotNull final Set<Tag> tags) {\n        this.tags.addAll(tags);\n    }\n\n    @Override\n    public boolean test(final Transaction transaction) {\n        if (tags.isEmpty()) {\n            return true;\n        }\n\n        for (final Tag tag : tags) {\n            if (transaction.getTags().contains(tag)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/function/TransactionAgePredicate.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.function;\n\nimport java.time.LocalDate;\nimport java.time.temporal.ChronoUnit;\nimport java.util.function.Predicate;\n\nimport jgnash.engine.Transaction;\n\n/**\n * Predicate for age of a {@code Transaction}.\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionAgePredicate implements Predicate<Transaction> {\n\n    private final int age;\n    private final ChronoUnit chronoUnit;\n\n    public TransactionAgePredicate(final ChronoUnit chronoUnit, final int age) {\n        this.chronoUnit = chronoUnit;\n        this.age = age;\n    }\n\n    @Override\n    public boolean test(final Transaction transaction) {\n        if (chronoUnit == ChronoUnit.FOREVER) {\n            return true;\n        }\n\t\treturn chronoUnit.between(transaction.getLocalDate(), LocalDate.now()) <= age;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/prefs/MapBasedPreferences.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.prefs;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.prefs.AbstractPreferences;\n\n/**\n * Map based Preferences implementation.  Preferences must be persisted using the\n * {@code exportSubtree(OutputStream os)} and {@code Preferences.importPreferences(InputStream)}\n * methods.\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"unused\")\nclass MapBasedPreferences extends AbstractPreferences {\n\n    private final boolean isUserNode;\n\n    private final Map<String, String> map = new HashMap<>();\n\n    MapBasedPreferences(final MapBasedPreferences parent, final String name, final boolean isUserNode) {\n        super(parent, name);\n        this.isUserNode = isUserNode;\n        newNode = true;\n    }\n\n    @Override\n    public boolean isUserNode() {\n        return isUserNode;\n    }\n\n    @Override\n    protected void putSpi(final String key, final String value) {\n        map.put(key, value);\n    }\n\n    @Override\n    protected String getSpi(final String key) {\n        return map.get(key);\n    }\n\n    @Override\n    protected void removeSpi(final String key) {\n        map.remove(key);\n    }\n\n    @Override\n    protected void removeNodeSpi() {\n        map.clear();\n    }\n\n    @Override\n    protected String[] keysSpi() {\n        return map.keySet().toArray(new String[0]);\n    }\n\n    @Override\n    protected String[] childrenNamesSpi() {\n        return new String[0];\n    }\n\n    @Override\n    protected AbstractPreferences childSpi(final String name) {\n        return new MapBasedPreferences(this, name, isUserNode);\n    }\n\n    @Override\n    protected void syncSpi() {\n        // implementation not needed, memory based\n    }\n\n    @Override\n    protected void flushSpi() {\n        // implementation not needed, memory based\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/prefs/MapPreferencesFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.prefs;\n\nimport java.util.prefs.Preferences;\nimport java.util.prefs.PreferencesFactory;\n\n/**\n * Map based Preferences implementation.\n *\n * @author Craig Cavanaugh*\n */\n@SuppressWarnings(\"unused\")\npublic class MapPreferencesFactory implements PreferencesFactory {\n\n    private static final Preferences user = new MapBasedPreferences(null, \"\", true);\n\n    private static final Preferences system = new MapBasedPreferences(null, \"\", false);\n\n    /**\n     * Returns the system root preference node.  (Multiple calls on this\n     * method will return the same object reference.)\n     */\n    @Override\n    public Preferences systemRoot() {\n        return system;\n    }\n\n    /**\n     * Returns the user root preference node corresponding to the calling\n     * user.  In a server, the returned value will typically depend on\n     * some implicit client-context.\n     */\n    @Override\n    public Preferences userRoot() {\n        return user;\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/java/jgnash/util/prefs/PortablePreferences.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util.prefs;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.util.FileUtils;\nimport jgnash.util.LogUtil;\nimport jgnash.util.NotNull;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.BackingStoreException;\nimport java.util.prefs.InvalidPreferencesFormatException;\nimport java.util.prefs.Preferences;\n\n/**\n * Portable preferences utility class.\n *\n * @author Craig Cavanaugh\n */\npublic class PortablePreferences {\n\n    private PortablePreferences() {\n        // Utility class\n    }\n\n    public static void initPortablePreferences(final String portableFile) {\n        System.setProperty(\"java.util.prefs.PreferencesFactory\", \"jgnash.util.prefs.MapPreferencesFactory\");\n\n        importPreferences(portableFile);\n\n        // add a shutdown hook to export user preferences\n        Runtime.getRuntime().addShutdownHook(new ExportPreferencesThread(portableFile));\n    }\n\n    private static void importPreferences(final String file) {\n        final Path importFile = getPreferenceFile(file);\n\n        if (Files.isReadable(importFile)) {\n            Logger.getLogger(PortablePreferences.class.getName()).info(\"Importing preferences\");\n\n            try (final InputStream is = Files.newInputStream(importFile)) {\n                Preferences.importPreferences(is);\n            } catch (final InvalidPreferencesFormatException | IOException e) {\n                System.err.println(\"Preferences file \" + importFile + \" could not be read\");\n                LogUtil.logSevere(PortablePreferences.class, e);\n            }\n        } else {\n            System.err.println(\"Preferences file \" + importFile + \" was not found\");\n        }\n    }\n\n    public static void deleteUserPreferences() {\n        try {\n            final Preferences p = Preferences.userRoot();\n            if (p.nodeExists(\"/jgnash\")) {\n                Preferences jgnash = p.node(\"/jgnash\");\n                jgnash.removeNode();\n                p.flush();\n                System.out.println(ResourceUtils.getString(\"Message.UninstallGood\"));\n            } else {\n                System.err.println(ResourceUtils.getString(\"Message.PrefFail\"));\n            }\n        } catch (final BackingStoreException bse) {\n            LogUtil.logSevere(PortablePreferences.class, bse);\n            System.err.println(ResourceUtils.getString(\"Message.UninstallBad\"));\n        }\n    }\n\n    private static Path getPreferenceFile(final String portableFile) {\n        final Path exportFile;\n\n        if (portableFile != null && !portableFile.isEmpty()) {\n            exportFile = Paths.get(portableFile);\n        } else {\n            exportFile = Paths.get(System.getProperty(\"user.dir\") + FileUtils.SEPARATOR + \"pref.xml\");\n        }\n        Logger.getLogger(PortablePreferences.class.getName()).log(Level.INFO, \"Preference file: {0}\", exportFile);\n        return exportFile;\n    }\n\n    private static class ExportPreferencesThread extends Thread {\n        final String file;\n\n        ExportPreferencesThread(@NotNull final String file) {\n            this.file = file;\n        }\n\n        @Override\n        public void run() {\n            Logger.getLogger(ExportPreferencesThread.class.getName()).info(\"Exporting preferences\");\n\n            final Path exportFile = getPreferenceFile(file);\n\n            final Preferences preferences = Preferences.userRoot();\n\n            try (final OutputStream os = Files.newOutputStream(exportFile)) {\n                try {\n                    if (preferences.nodeExists(\"/jgnash\")) {\n                        final Preferences p = preferences.node(\"/jgnash\");\n                        p.exportSubtree(os);\n                    }\n                    deleteUserPreferences();\n                } catch (final BackingStoreException e) {\n                    LogUtil.logSevere(PortablePreferences.class, e);\n                }\n            } catch (final IOException e) {\n                LogUtil.logSevere(ExportPreferencesThread.class, e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-core/src/main/resources/META-INF/persistence.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<persistence xmlns=\"http://java.sun.com/xml/ns/persistence\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xsi:schemaLocation=\"http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd\"\n             version=\"2.0\">\n\n    <persistence-unit name=\"jgnash\" transaction-type=\"RESOURCE_LOCAL\">\n\n        <provider>\n            org.hibernate.jpa.HibernatePersistenceProvider\n        </provider>\n\n        <!-- We must enumerate each entity in the persistence unit -->\n        <class>jgnash.engine.AbstractInvestmentTransactionEntry</class>\n        <class>jgnash.engine.Account</class>\n        <class>jgnash.engine.AmortizeObject</class>\n        <class>jgnash.engine.budget.Budget</class>\n        <class>jgnash.engine.budget.BudgetGoal</class>\n        <class>jgnash.engine.CommodityNode</class>\n        <class>jgnash.engine.Config</class>\n        <class>jgnash.engine.CurrencyNode</class>\n        <class>jgnash.engine.ExchangeRate</class>\n        <class>jgnash.engine.ExchangeRateHistoryNode</class>\n        <class>jgnash.engine.InvestmentTransaction</class>\n        <class>jgnash.engine.RootAccount</class>\n        <class>jgnash.engine.SecurityHistoryEvent</class>\n        <class>jgnash.engine.SecurityHistoryNode</class>\n        <class>jgnash.engine.SecurityNode</class>\n        <class>jgnash.engine.Tag</class>\n        <class>jgnash.engine.Transaction</class>\n        <class>jgnash.engine.TransactionEntry</class>\n        <class>jgnash.engine.TransactionEntryAddX</class>\n        <class>jgnash.engine.TransactionEntryAbstractIncrease</class>\n        <class>jgnash.engine.TransactionEntryBuyX</class>\n        <class>jgnash.engine.TransactionEntryDividendX</class>\n        <class>jgnash.engine.TransactionEntryMergeX</class>\n        <class>jgnash.engine.TransactionEntryReinvestDivX</class>\n        <class>jgnash.engine.TransactionEntryRemoveX</class>\n        <class>jgnash.engine.TransactionEntryRocX</class>\n        <class>jgnash.engine.TransactionEntrySellX</class>\n        <class>jgnash.engine.TransactionEntrySplitX</class>\n        <class>jgnash.engine.recurring.DailyReminder</class>\n        <class>jgnash.engine.recurring.MonthlyReminder</class>\n        <class>jgnash.engine.recurring.OneTimeReminder</class>\n        <class>jgnash.engine.recurring.Reminder</class>\n        <class>jgnash.engine.recurring.WeeklyReminder</class>\n        <class>jgnash.engine.recurring.YearlyReminder</class>\n        <class>jgnash.engine.StoredObject</class>\n        <class>jgnash.engine.TrashObject</class>\n        <class>jgnash.engine.jpa.JpaTrashEntity</class>\n\n        <properties>\n            <property name=\"javax.persistence.jdbc.password\" value=\"\"/>\n            <property name=\"javax.persistence.jdbc.user\" value=\"\"/>\n            <property name=\"javax.persistence.lock.timeout\" value=\"30000\"/> <!-- Allow a 30 second stall for now -->\n\n            <property name=\"hibernate.hbm2ddl.auto\" value=\"update\"/>\n\n            <property name=\"hibernate.connection.provider_class\"\n                      value=\"org.hibernate.hikaricp.internal.HikariCPConnectionProvider\" />\n\n            <property name=\"hibernate.hikari.minimumIdle\" value=\"5\" />\n            <property name=\"hibernate.hikari.maximumPoolSize\" value=\"10\" />\n            <property name=\"hibernate.hikari.idleTimeout\" value=\"30000\" />\n\n            <!--<property name=\"hibernate.generate_statistics\" value=\"true\"/>\n            <property name=\"org.hibernate.stat\" value=\"DEBUG\"/>-->\n\n        </properties>\n    </persistence-unit>\n\n</persistence>\n"
  },
  {
    "path": "jgnash-core/src/main/resources/logging.properties",
    "content": "handlers= java.util.logging.ConsoleHandler\n.level= INFO\njava.util.logging.ConsoleHandler.level = INFO\njava.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\njava.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] {%2$s} %5$s%6$s%n"
  },
  {
    "path": "jgnash-fx/build.gradle.kts",
    "content": "description = \"jGnash\"\n\nval javaFXVersion: String by project    // extract JavaFX version from gradle.properties\nval picocliVersion: String by project\nval testFxVersion: String by project\nval monocleVersion: String by project\nval commonsLangVersion: String by project\nval commonsMathVersion: String by project\n\nval junitVersion: String by project\nval junitExtensionsVersion: String by project\nval awaitilityVersion: String by project\n\nplugins {\n    application // creates a task to run the full application\n    `java-library`\n    id(\"org.openjfx.javafxplugin\")\n    id(\"edu.sc.seis.macAppBundle\")\n}\n\nval jGnashVersion : String = version.toString()\n\napplication {\n    mainClassName = \"jgnash.app.jGnash\"\n}\n\ndependencies {\n    testImplementation(\"org.junit.jupiter:junit-jupiter-api:$junitVersion\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter-params:$junitVersion\")\n    testRuntimeOnly(\"org.junit.jupiter:junit-jupiter-engine:$junitVersion\")\n    testImplementation(\"io.github.glytching:junit-extensions:$junitExtensionsVersion\")\n    testImplementation(\"org.awaitility:awaitility:$awaitilityVersion\")\n\n    implementation(project(\":jgnash-resources\"))\n    implementation(project(\":jgnash-core\"))\n    implementation(project(\":jgnash-convert\"))\n    implementation(project(\":jgnash-report-core\"))\n    implementation(project(\":jgnash-plugin\"))\n\n    implementation(\"info.picocli:picocli:$picocliVersion\")\n\n    implementation(\"org.apache.commons:commons-lang3:$commonsLangVersion\")\n    implementation(\"org.apache.commons:commons-math3:$commonsMathVersion\")\n\n    // Hack to include all javafx platforms in the classpath\n    // The platform specific libraries are excluded when the distribution is assembled\n    implementation(\"org.openjfx:javafx-base:$javaFXVersion\")\n    implementation(\"org.openjfx:javafx-fxml:$javaFXVersion\")\n    implementation(\"org.openjfx:javafx-controls:$javaFXVersion\")\n    implementation(\"org.openjfx:javafx-graphics:$javaFXVersion\")\n    implementation(\"org.openjfx:javafx-media:$javaFXVersion\")\n    implementation(\"org.openjfx:javafx-swing:$javaFXVersion\")\n    implementation(\"org.openjfx:javafx-web:$javaFXVersion\")\n\n    runtimeOnly(\"org.openjfx:javafx-base:$javaFXVersion:linux\")\n    runtimeOnly(\"org.openjfx:javafx-fxml:$javaFXVersion:linux\")\n    runtimeOnly(\"org.openjfx:javafx-controls:$javaFXVersion:linux\")\n    runtimeOnly(\"org.openjfx:javafx-graphics:$javaFXVersion:linux\")\n    runtimeOnly(\"org.openjfx:javafx-media:$javaFXVersion:linux\")\n    runtimeOnly(\"org.openjfx:javafx-swing:$javaFXVersion:linux\")\n    runtimeOnly(\"org.openjfx:javafx-web:$javaFXVersion:linux\")\n\n    runtimeOnly(\"org.openjfx:javafx-base:$javaFXVersion:win\")\n    runtimeOnly(\"org.openjfx:javafx-fxml:$javaFXVersion:win\")\n    runtimeOnly(\"org.openjfx:javafx-controls:$javaFXVersion:win\")\n    runtimeOnly(\"org.openjfx:javafx-graphics:$javaFXVersion:win\")\n    runtimeOnly(\"org.openjfx:javafx-media:$javaFXVersion:win\")\n    runtimeOnly(\"org.openjfx:javafx-swing:$javaFXVersion:win\")\n    runtimeOnly(\"org.openjfx:javafx-web:$javaFXVersion:win\")\n\n    runtimeOnly(\"org.openjfx:javafx-base:$javaFXVersion:mac\")\n    runtimeOnly(\"org.openjfx:javafx-fxml:$javaFXVersion:mac\")\n    runtimeOnly(\"org.openjfx:javafx-controls:$javaFXVersion:mac\")\n    runtimeOnly(\"org.openjfx:javafx-graphics:$javaFXVersion:mac\")\n    runtimeOnly(\"org.openjfx:javafx-media:$javaFXVersion:mac\")\n    runtimeOnly(\"org.openjfx:javafx-swing:$javaFXVersion:mac\")\n    runtimeOnly(\"org.openjfx:javafx-web:$javaFXVersion:mac\")\n    // end hack\n\n    // required of Unit testing JavaFX\n    testImplementation(\"org.testfx:testfx-junit5:$testFxVersion\")\n    testImplementation(\"org.testfx:openjfx-monocle:$monocleVersion\")\n}\n\njavafx {\n    version = javaFXVersion\n    modules(\"javafx.base\", \"javafx.controls\", \"javafx.fxml\", \"javafx.web\", \"javafx.swing\",\n            \"javafx.graphics\", \"javafx.media\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n\n    // we want display the following test events\n    testLogging {\n        events(\"PASSED\", \"STARTED\", \"FAILED\", \"SKIPPED\")\n        showStandardStreams = true\n    }\n}\n\ntasks.startScripts {\n    applicationName = \"bootloader\"\n}\n\ntasks.distZip {\n    destinationDirectory.set(file(rootDir))\n\n    // this \"should\" work according to Gradle Doc but mangles the content of the zip file\n    //archiveFileName.set(\"jgnash-${archiveVersion.get()}-bin.${archiveExtension.get()}\")\n\n    // build the mt940 plugin prior to creating the zip file without creating a circular loop\n    dependsOn(\":mt940:jar\")\n\n    // add the mt940 plugin\n    into(\"jGnash-${archiveVersion.get()}\") {\n        from(\"../mt940/build/libs\")\n        include(\"*\")\n        into(\"jGnash-${archiveVersion.get()}/plugins\")\n    }\n\n    into(\"jGnash-${archiveVersion.get()}\") {\n        from(\".\")\n        include(\"scripts/*\")\n    }\n\n    doLast {\n        // delete the old renamed build\n        file(\"${destinationDirectory.get()}/jgnash-${archiveVersion.get()}-bin.${archiveExtension.get()}\").delete()\n\n        file(\"${destinationDirectory.get()}/${archiveFileName.get()}\").renameTo(file(\"${destinationDirectory.get()}/jgnash-${archiveVersion.get()}-bin.${archiveExtension.get()}\"))\n    }\n}\n\ndistributions {\n    main {\n        distributionBaseName.set(\"jGnash\")\n\n        contents {\n            from(\"../jgnash-manual/src/Manual.pdf\")\n            from(\"../changelog.adoc\")\n            from(\"../rust-launcher/target/release/jGnash.exe\")\n            from(\"../README.html\")\n            from(\"../README.adoc\")\n            from(\"../jGnash\")\n            exclude(\"**/*-linux*\")  // excludes linux specific JavaFx modules from cross platform zip\n            exclude(\"**/*-win*\")    // excludes windows specific JavaFx modules from cross platform zip\n            exclude(\"**/*-mac*\")    // excludes mac specific JavaFx modules from cross platform zip\n        }\n    }\n}\n\nmacAppBundle {\n    appStyle = \"universalJavaApplicationStub\"\n    appName = \"jGnash-$jGnashVersion\"\n    mainClassName = \"jgnash.app.jGnash\"\n    icon = \"../deployfx/gnome-money.icns\"\n    javaProperties[\"apple.laf.useScreenMenuBar\"] = \"true\"\n}\n\n/**\n * Returns a proper Class-Path entry for the manifest file\n * @return classpath relative to the installation root point to the jars in the lib directory\n */\nfun generateManifestClassPath(): String {\n    val path = StringBuilder()\n\n    configurations.runtimeClasspath.get().files.forEach {\n        path.append(\"lib/\")\n        path.append(it.name)\n        path.append(\" \")\n    }\n\n    return path.toString()\n}\n\ntasks.jar {\n    // Keep jar clean:\n    exclude(\"META-INF/*.SF\", \"META-INF/*.DSA\", \"META-INF/*.RSA\", \"META-INF/*.MF\")\n\n    manifest {\n        attributes(mapOf(\"Main-Class\" to \"jGnash\", \"Class-Path\" to generateManifestClassPath()))\n    }\n}\n\ntasks.register(\"macDist\") {\n    description = \"Creates a Mac compatible .app distribution directory\"\n    dependsOn(\"createApp\", \"distZip\")\n\n    doLast {\n        configurations.runtimeClasspath.get().files.forEach {\n            // copy all files in the class path, but ignore windows and linux specific files\n            if (!it.name.contains(\"linux.jar\") && !it.name.contains(\"win.jar\")) {\n                it.copyTo(file(\"$buildDir/macApp/jGnash-$jGnashVersion.app/Contents/Java/\" + it.name), true)\n            }\n        }\n    }\n}\n\ntasks.register<Zip>(\"macDistZip\") {\n    description = \"Creates a Mac compatible archive of the .app distribution directory\"\n\n    dependsOn(\"clean\", \"macDist\")\n    archiveFileName.set(\"jGnash-$jGnashVersion.App.zip\")\n    destinationDirectory.set(rootDir)\n\n    from(\"$buildDir/macApp\")\n\n    from(\"../jgnash-manual/src/Manual.pdf\") {\n        into(\"jGnash-$jGnashVersion.app/Contents/SharedSupport\")\n    }\n\n    from(\"../changelog.adoc\") {\n        into(\"jGnash-$jGnashVersion.app/Contents/SharedSupport\")\n    }\n\n    from(\"../README.adoc\") {\n        into(\"jGnash-$jGnashVersion.app/Contents/SharedSupport\")\n    }\n\n    from(\"../README.html\") {\n        into(\"jGnash-$jGnashVersion.app/Contents/SharedSupport\")\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/scripts/clean-security-history.js",
    "content": "// clears security history nodes that fall on weekends\n\nvar LocalDate = Packages.java.time.LocalDate;\nvar DayOfWeek = Packages.java.time.DayOfWeek;\n\nvar EngineFactory = Packages.jgnash.engine.EngineFactory;\n\nfunction debug(message) {   // helper function to print messages to the console\n    Java.type(\"java.lang.System\").out[\"println(String)\"](message);\n}\n\nvar Console = Java.type(\"jgnash.uifx.views.main.ConsoleDialogController\");\n\n//show the console dialog to see the debug information\nConsole.show();\n\n// this is how to get the default Engine instance\nvar engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\nif (engine !== null) {\n\n    // List<SecurityNode>\n    var securities = engine.getSecurities();\n\n    for (var i = 0; i < securities.size(); i++) {\n        var security = securities.get(i);\n        debug(security.getSymbol());\n\n        var securityHistory = securities.get(i).getHistoryNodes();\n\n        for (var j = 0; j < securityHistory.size(); j++) {\n            var historyNode = securityHistory.get(j);\n            var dayOfWeek = historyNode.getLocalDate().getDayOfWeek();\n\n            if (dayOfWeek === DayOfWeek.SATURDAY || dayOfWeek === DayOfWeek.SUNDAY) {\n                debug(\"removing one\");\n                engine.removeSecurityHistory(security, historyNode.getLocalDate());\n            }\n        }\n    }\n}\n\ndebug(\"finished\");\n"
  },
  {
    "path": "jgnash-fx/scripts/helloworld.js",
    "content": "\nfunction debug(message) {   // helper function to print messages to the console\n    Java.type(\"java.lang.System\").out[\"println(String)\"](message);\n}\n\nvar Console = Java.type(\"jgnash.uifx.views.main.ConsoleDialogController\");\n\n//show the console dialog to see the debug information\nConsole.show();\n\ndebug(\"Hello World!\");\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/app/jGnash.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.app;\n\nimport jgnash.bootloader.BootLoader;\nimport jgnash.bootloader.BootLoaderDialog;\n\nimport javax.swing.JOptionPane;\n\nimport java.awt.EventQueue;\n\nimport picocli.CommandLine;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Bootstraps a modular jGnashFx by downloading platform specific OpenJFX libraries and then launching the application\n *\n * @author Craig Cavanaugh\n */\npublic class jGnash {\n\n    public static void main(final String[] args) {\n\n        boolean bypassBootLoader = false;\n\n        final CommandLine commandLine = new CommandLine(new jGnashFx.CommandLineOptions());\n        commandLine.setToggleBooleanFlags(false);\n        commandLine.setUsageHelpWidth(80);\n\n        try {\n            final CommandLine.ParseResult pr = commandLine.parseArgs(args);\n            final jGnashFx.CommandLineOptions options = commandLine.getCommand();\n\n            if (CommandLine.printHelpIfRequested(pr)) {\n                System.exit(0);\n            }\n\n            bypassBootLoader = options.bypassBootloader;\n        } catch (final CommandLine.UnmatchedArgumentException uae) {\n            commandLine.usage(System.err, CommandLine.Help.Ansi.AUTO);\n            System.exit(1);\n        } catch (final Exception e) {\n            logSevere(jGnash.class, e);\n            System.exit(1);\n        }\n\n        if (BootLoader.getOS() != null) {\n            if (bypassBootLoader || BootLoader.doFilesExist()) {\n                launch(args);\n            } else {\n\n                EventQueue.invokeLater(() -> {\n                    final BootLoaderDialog d = new BootLoaderDialog();\n                    d.setVisible(true);\n\n                    final Thread thread = new Thread(() -> {\n                        if (!BootLoader.downloadFiles(d.getActiveFileConsumer(), d.getPercentCompleteConsumer())) {\n                            System.exit(-1);\n                        }\n                    });\n\n                    thread.start();\n                });\n            }\n\n        } else {\n            JOptionPane.showMessageDialog(null, \"Unsupported operating system\", \"Error\",\n                    JOptionPane.ERROR_MESSAGE);\n            System.exit(BootLoader.FAILED_EXIT);\n        }\n    }\n\n    private static void launch(final String[] args) {\n        jGnashFx.main(args);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/app/jGnashFx.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.app;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.net.Authenticator;\nimport java.util.Arrays;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javax.swing.JOptionPane;\n\nimport javafx.application.Application;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.jpa.JpaNetworkServer;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.resource.util.OS;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.Version;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.net.NetworkAuthenticator;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.FileUtils;\nimport jgnash.util.LogUtil;\nimport jgnash.util.prefs.PortablePreferences;\n\nimport picocli.CommandLine;\n\nimport static picocli.CommandLine.Command;\nimport static picocli.CommandLine.Help;\nimport static picocli.CommandLine.IVersionProvider;\nimport static picocli.CommandLine.Option;\nimport static picocli.CommandLine.ParseResult;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Main entry point for the application.\n * <p>\n * This bootstraps the JavaFX application and lives in the default class as a workaround for\n * Gnome and OSX menu naming issues.\n *\n * @author Craig Cavanaugh\n */\npublic class jGnashFx extends Application {\n\n    private static File dataFile = null;\n\n    private static File serverFile = null;\n\n    private static char[] password = EngineFactory.EMPTY_PASSWORD;\n\n    private static int port = JpaNetworkServer.DEFAULT_PORT;\n\n    private static String hostName = null;\n\n    public static void main(final String[] args) {\n\n        if (OS.getJavaVersion() < 11f) {\n            System.err.println(ResourceUtils.getString(\"Message.JVM11\"));\n            System.err.println(ResourceUtils.getString(\"Message.Version\") + \" \"\n                    + System.getProperty(\"java.version\") + \"\\n\");\n\n            // show a swing based dialog\n            JOptionPane.showMessageDialog(null, ResourceUtils.getString(\"Message.JVM11\"),\n                    ResourceUtils.getString(\"Title.Error\"), JOptionPane.ERROR_MESSAGE);\n            return;\n        }\n\n        // Register the default exception handler\n        Thread.setDefaultUncaughtExceptionHandler(new StaticUIMethods.ExceptionHandler());\n\n        final CommandLine commandLine = new CommandLine(new CommandLineOptions());\n        commandLine.setToggleBooleanFlags(false);\n        commandLine.setUsageHelpWidth(80);\n\n        try {\n\n            final ParseResult pr = commandLine.parseArgs(args);\n            final CommandLineOptions options = commandLine.getCommand();\n\n            if (CommandLine.printHelpIfRequested(pr)) {\n                System.exit(0);\n            }\n\n            // check for bad server file and hostName combination... can't do both\n            if (serverFile != null && options.hostName != null) {\n                commandLine.usage(System.err, Help.Ansi.AUTO);\n                System.exit(1);\n            }\n\n            configureLogging();\n\n            if (options.uninstall) {\n                PortablePreferences.deleteUserPreferences();\n                System.exit(0);\n            }\n\n\n            //System.getProperties().put(EncryptionManager.ENCRYPTION_FLAG, Boolean.toString(options.ssl));\n            //System.getProperties().put(\"ssl\", Boolean.toString(options.ssl));\n\n            port = options.port;\n            hostName = options.hostName;\n            password = options.password;\n\n            if (options.shutdown) {\n                if (hostName == null) {\n                    hostName = EngineFactory.LOCALHOST;\n                }\n                MessageBus.getInstance().shutDownRemoteServer(hostName, port + 1, password);\n                System.exit(0);\n            }\n\n            if (options.verbose) {\n                System.setProperty(\"javafx.verbose\", \"true\");\n            }\n\n            if (options.portableFile != null) {\n                PortablePreferences.initPortablePreferences(options.portableFile.getAbsolutePath());\n            } else if (options.portable) {\n                PortablePreferences.initPortablePreferences(null);\n            }\n\n            if (options.zeroArgFile != null && options.zeroArgFile.exists()) {\n                jGnashFx.dataFile = options.zeroArgFile;\n            }\n\n            if (options.dataFile != null && options.dataFile.exists()) {\n                jGnashFx.dataFile = options.dataFile;\n            }\n\n            if (options.serverFile != null && options.serverFile.exists()) {\n                jGnashFx.serverFile = options.serverFile;\n                startServer();\n            } else {\n                setupNetworking();\n                launch(args);\n            }\n        } catch (final Exception e) {\n            logSevere(jGnashFx.class, e);\n            commandLine.usage(System.err, Help.Ansi.AUTO);\n            System.exit(1);\n        }\n    }\n\n    @Override\n    public void start(final Stage primaryStage) throws Exception {\n        final MainView mainView = new MainView();\n        mainView.start(primaryStage, dataFile, password, hostName, port);\n\n        if (password != null) {\n            Arrays.fill(password, (char) 0);    // clear the password to protect against malicious code\n        }\n    }\n\n    @Override\n    public void stop() {\n        System.exit(0); // Platform.exit() is not always enough for a complete shutdown, force closure\n    }\n\n    private static void startServer() {\n        try {\n            if (!FileUtils.isFileLocked(serverFile.getAbsolutePath())) {\n                JpaNetworkServer networkServer = new JpaNetworkServer();\n                networkServer.startServer(serverFile.getAbsolutePath(), port, password);\n                Arrays.fill(password, (char) 0);    // clear the password to protect against malicious code\n            } else {\n                System.err.println(ResourceUtils.getString(\"Message.FileIsLocked\"));\n            }\n        } catch (final FileNotFoundException e) {\n            logSevere(jGnashFx.class, e);\n            System.err.println(\"File \" + serverFile.getAbsolutePath() + \" was not found\");\n        } catch (final Exception e) {\n            logSevere(jGnashFx.class, e);\n        }\n    }\n\n    private static void setupNetworking() {\n        final Preferences auth = Preferences.userRoot().node(NetworkAuthenticator.NODEHTTP);\n\n        if (auth.getBoolean(NetworkAuthenticator.USEPROXY, false)) {\n\n            final String proxyHost = auth.get(NetworkAuthenticator.PROXYHOST, \"\");\n            final String proxyPort = auth.get(NetworkAuthenticator.PROXYPORT, \"\");\n\n            System.getProperties().put(\"http.proxyHost\", proxyHost);\n            System.getProperties().put(\"http.proxyPort\", proxyPort);\n\n            // this will deal with any authentication requests properly\n            Authenticator.setDefault(new NetworkAuthenticator());\n\n            System.out.println(ResourceUtils.getString(\"Message.Proxy\") + proxyHost + \":\" + proxyPort);\n        }\n    }\n\n    private static void configureLogging() {\n        LogUtil.configureLogging();\n\n        final Handler[] handlers = Logger.getLogger(\"\").getHandlers();\n        for (final Handler handler : handlers) {\n            handler.setLevel(Level.ALL);\n        }\n\n        Engine.getLogger().setLevel(Level.ALL);\n    }\n\n    @SuppressWarnings({\"CanBeFinal\", \"FieldMayBeFinal\"})\n    @Command(mixinStandardHelpOptions = true, versionProvider = CommandLineOptions.VersionProvider.class, name = \"jGnash\", separator = \" \", sortOptions = false)\n    public static class CommandLineOptions {\n\n        private static final String FILE_OPTION_SHORT = \"-f\";\n        private static final String FILE_OPTION_LONG = \"--file\";\n        private static final String VERBOSE_OPTION_SHORT = \"-v\";\n        private static final String VERBOSE_OPTION_LONG = \"--verbose\";\n        private static final String PORTABLE_FILE_OPTION = \"--portableFile\";\n        private static final String PORTABLE_OPTION_SHORT = \"-p\";\n        private static final String PORTABLE_OPTION_LONG = \"--portable\";\n        private static final String UNINSTALL_OPTION_SHORT = \"-u\";\n        private static final String UNINSTALL_OPTION_LONG = \"--uninstall\";\n        private static final String PORT_OPTION = \"--port\";\n        private static final String HOST_OPTION = \"--hostName\";\n        private static final String PASSWORD_OPTION = \"--password\";\n        private static final String SERVER_OPTION = \"--server\";\n        private static final String SHUTDOWN_OPTION = \"--shutdown\";\n        private static final String BYPASS_BOOTLOADER = \"--bypassBootloader\";\n        //private static final String SSL_OPTION = \"--ssl\";\n\n        @CommandLine.Parameters(index = \"0\", arity = \"0\")\n        private File zeroArgFile = null;\n\n        @Option(names = {FILE_OPTION_SHORT, FILE_OPTION_LONG}, paramLabel = \"<File>\", description = \"File to load at start\")\n        private File dataFile = null;\n\n        @Option(names = {PASSWORD_OPTION}, description = \"Password for a local File, server or client\")\n        private char[] password = EngineFactory.EMPTY_PASSWORD; // must not be null\n\n        @Option(names = {PORTABLE_OPTION_SHORT, PORTABLE_OPTION_LONG}, description = \"Enable portable preferences\")\n        private boolean portable = false;\n\n        @Option(names = {PORTABLE_FILE_OPTION}, paramLabel = \"<File>\", description = \"Enable portable preferences and specify the file\")\n        private File portableFile = null;\n\n        @Option(names = {HOST_OPTION}, description = \"Server host name or address\")\n        private String hostName = null;\n\n        @Option(names = {PORT_OPTION}, description = \"Network port server is running on (default: \" + JpaNetworkServer.DEFAULT_PORT + \")\")\n        private int port = JpaNetworkServer.DEFAULT_PORT;\n\n        //@Option(names = {SSL_OPTION}, description = \"Enable SSL for network communication\")\n        //private boolean ssl = false;\n\n        @Option(names = {SERVER_OPTION}, paramLabel = \"<File>\", description = \"Runs as a server using the specified file\")\n        private File serverFile = null;\n\n        @Option(names = {SHUTDOWN_OPTION}, description = \"Issues a shutdown request to a server\")\n        private boolean shutdown = false;\n\n        @Option(names = {UNINSTALL_OPTION_SHORT, UNINSTALL_OPTION_LONG}, description = \"Remove registry settings (uninstall)\")\n        private boolean uninstall = false;\n\n        @Option(names = {VERBOSE_OPTION_SHORT, VERBOSE_OPTION_LONG}, description = \"Enable verbose application messages\")\n        private boolean verbose = false;\n\n        @CommandLine.Option(names = {BYPASS_BOOTLOADER}, description = \"Bypasses the bootloader and requires manual installation of OS specific files\")\n        public boolean bypassBootloader = false;\n\n        static class  VersionProvider implements IVersionProvider {\n            @Override\n            public String[] getVersion() {\n                return new String[] {Version.getAppName() + \" \" + Version.getAppVersion()};\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/bootloader/BootLoader.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.bootloader;\n\nimport jgnash.util.NotNull;\n\nimport javax.swing.JOptionPane;\nimport javax.xml.bind.DatatypeConverter;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.channels.Channels;\nimport java.nio.channels.ReadableByteChannel;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.security.MessageDigest;\nimport java.text.MessageFormat;\nimport java.util.Arrays;\nimport java.util.function.Consumer;\nimport java.util.function.IntConsumer;\nimport java.util.function.LongConsumer;\nimport java.util.logging.Logger;\n\n/**\n * Boot loader used to download platform specific OpenJFX files and place them in the LIB directory.\n *\n * @author Craig Cavanaugh\n */\npublic class BootLoader {\n\n    private static final String JFX_VERSION = \"15.0.1\";\n\n    private static final String MAVEN_REPO = \"https://repo1.maven.org/maven2/org/openjfx/\";\n\n    private static final String FILE_PATTERN = \"javafx-{0}-{1}-{2}.jar\";\n\n    private static final String SEPARATOR = System.getProperty(\"file.separator\");\n\n    private static final String OS = getOS();\n\n    public static final int FAILED_EXIT = -1;\n\n    static final int REBOOT_EXIT = 100;\n\n    private static final int BUFFER_SIZE = 4096;\n\n    // download them all\n    private static final String[] JARS = new String[]{\"base\", \"controls\", \"fxml\", \"graphics\", \"media\", \"swing\", \"web\"};\n\n    public static boolean doFilesExist() {\n        String libPath = getLibPath();\n\n        boolean result = true;\n\n        final String pathSpec = libPath + SEPARATOR + FILE_PATTERN;\n\n        for (final String fxJar : JARS) {\n            Path path = Paths.get(MessageFormat.format(pathSpec, fxJar, JFX_VERSION, OS));\n            if (!Files.exists(path)) {\n                result = false;\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    private static String getLibPath() {\n        // Current class lives in a .jar that lives in the lib folder we want to populate.  Let's\n        // use that to find the lib path rather than depend on jGnash being invoked from the correct\n        // folder.\n        // https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file?noredirect=1&lq=1\n        try {\n            return new File(BootLoader.class.getProtectionDomain().getCodeSource().getLocation().toURI())\n                           .getParentFile().getPath();\n        } catch (final URISyntaxException e) {\n            throw new RuntimeException(\"Unable to determine lib path fpr bootloader\");\n        }\n    }\n\n    public static String getOS() {\n        final String os = System.getProperty(\"os.name\");\n\n        if (os.startsWith(\"Linux\")) {\n            return \"linux\";\n        } else if (os.startsWith(\"Windows\")) {\n            return \"win\";\n        } else if (os.startsWith(\"Darwin\") || os.startsWith(\"Mac\")) {\n            return \"mac\";\n        }\n\n        return null;\n    }\n\n    public static boolean downloadFiles(final Consumer<String> fileNameConsumer, final IntConsumer percentCompleteConsumer) {\n        percentCompleteConsumer.accept(0);\n        boolean result = true;\n\n        final Path lib = Paths.get(BootLoader.getLibPath());\n\n        try {\n            final long completeDownloadSize = getTotalDownloadSize();\n\n            Files.createDirectories(lib);   // create the directory if needed\n\n            final String spec = MAVEN_REPO + \"javafx-{0}/{1}/\" + FILE_PATTERN;\n            final String pathSpec = lib + SEPARATOR + FILE_PATTERN;\n\n            final LongConsumer countConsumer = new LongConsumer() {\n                long totalCounts;\n\n                @Override\n                public void accept(final long value) {\n                    totalCounts += value;\n                    percentCompleteConsumer.accept((int) (((double) totalCounts / (double) completeDownloadSize) * 100));\n                }\n            };\n\n            for (final String fxJar : JARS) {\n                URL url = new URL(MessageFormat.format(spec, fxJar, JFX_VERSION, OS));\n                Path path = Paths.get(MessageFormat.format(pathSpec, fxJar, JFX_VERSION, OS));\n\n                if (!Files.exists(path)) {\n                    fileNameConsumer.accept(url.toExternalForm());\n\n                    try {\n                        boolean downloadResult = downloadFile(url, path, countConsumer);\n\n                        if (!downloadResult) {\n                            result = false;\n                            break;\n                        }\n                    } catch (final IOException e) {\n                        e.printStackTrace();\n                        showException(e);\n                    }\n                }\n            }\n        } catch (final IOException e) {\n            e.printStackTrace();\n            result = false;\n        }\n\n        return result;\n    }\n\n    private static long getTotalDownloadSize() throws MalformedURLException {\n        long size = 0;\n\n        for (String fxJar : JARS) {\n            final String spec = MAVEN_REPO + \"javafx-{0}/{1}/\" + FILE_PATTERN;\n            URL url = new URL(MessageFormat.format(spec, fxJar, JFX_VERSION, OS));\n\n            size += getFileDownloadSize(url);\n        }\n\n        return size;\n    }\n\n    private static long getFileDownloadSize(final URL source) {\n        long length = 0;\n\n        try {\n            final HttpURLConnection httpConnection = (HttpURLConnection) (source.openConnection());\n            length = httpConnection.getContentLength();\n\n            httpConnection.disconnect();\n        } catch (final IOException e) {\n            e.printStackTrace();\n        }\n\n        return length;\n    }\n\n    private static void showException(final Exception exception) {\n        final String message = exception.getMessage() + \"\\nStackTrace: \" + Arrays.toString(exception.getStackTrace());\n        final String title = exception.getClass().getName();\n\n        JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);\n    }\n\n    private static boolean downloadFile(final URL source, final Path dest, final LongConsumer countConsumer) throws IOException {\n        final Logger logger = Logger.getLogger(BootLoader.class.getName());\n        boolean result = true;\n\n        logger.info(\"Downloading \" + source.toExternalForm() + \" to \" + dest.toString());\n\n        String md5 = \"\";\n\n        HttpURLConnection httpConnection = (HttpURLConnection) (source.openConnection());\n\n        try (final BufferedInputStream in = new BufferedInputStream(httpConnection.getInputStream());\n             final BufferedOutputStream bout = new BufferedOutputStream(new CountingFileOutputStream(dest.toString(),\n                     countConsumer), BUFFER_SIZE)) {\n\n            byte[] data = new byte[BUFFER_SIZE];\n\n            int x;\n            while ((x = in.read(data, 0, BUFFER_SIZE)) >= 0) {\n                bout.write(data, 0, x);\n            }\n            bout.close();\n\n            // hash the file\n            final MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            md.update(Files.readAllBytes(dest));\n\n            md5 = DatatypeConverter.printHexBinary(md.digest()).toLowerCase();\n        } catch (final Exception e) {\n            e.printStackTrace();\n            showException(e);\n            result = false;\n        }\n\n        final URL md5Source = new URL(source.toExternalForm() + \".md5\");\n        final Path md5Dest = Files.createTempFile(\"\", \".md5\");\n\n        try (final ReadableByteChannel readableByteChannel = Channels.newChannel(md5Source.openStream());\n             final FileOutputStream fileOutputStream = new FileOutputStream(md5Dest.toString())) {\n            fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);\n        } catch (final Exception e) {\n            e.printStackTrace();\n            showException(e);\n        }\n\n        try (final BufferedReader reader = Files.newBufferedReader(md5Dest)) {\n            final String hash = reader.readLine();\n\n            if (hash.compareTo(md5) != 0) {\n                Files.delete(dest);\n                logger.severe(\"Download of \" + dest + \" was corrupt; removing the file\");\n                result = false;\n            }\n\n            Files.delete(md5Dest);\n        } catch (final Exception e) {\n            e.printStackTrace();\n            showException(e);\n        }\n\n        return result;\n    }\n\n    /**\n     * Updates a supplied consumer with the number of bytes written\n     */\n    private static class CountingFileOutputStream extends FileOutputStream {\n\n        final LongConsumer countConsumer;\n\n        CountingFileOutputStream(final String name, @NotNull final LongConsumer consumer) throws FileNotFoundException {\n            super(name);\n            this.countConsumer = consumer;\n        }\n\n        @Override\n        public void write(final int idx) throws IOException {\n            super.write(idx);\n            countConsumer.accept(1);\n        }\n\n        @Override\n        public void write(final byte[] b) throws IOException {\n            super.write(b);\n            countConsumer.accept(b.length);\n        }\n\n        @Override\n        public void write(final byte[] b, final int off, final int len) throws IOException {\n            super.write(b, off, len);\n            countConsumer.accept(len);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/bootloader/BootLoaderDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.bootloader;\n\nimport java.awt.EventQueue;\nimport java.awt.GridBagConstraints;\nimport java.awt.GridBagLayout;\nimport java.awt.Insets;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.IntConsumer;\n\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JProgressBar;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextArea;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * UI for displaying the boot loader download status.\n *\n * @author Craig Cavanaugh\n */\npublic class BootLoaderDialog extends JFrame {\n\n    private JLabel activeDownloadLabel;\n    private JProgressBar progressBar;\n\n    private final Consumer<String> activeFileConsumer;\n\n    private final IntConsumer percentCompleteConsumer;\n\n    public BootLoaderDialog() {\n\n        ImageIcon icon = new ImageIcon(Objects.requireNonNull(\n                BootLoaderDialog.class.getResource(\"/jgnash/resource/gnome-money.png\")));\n\n        setIconImage(icon.getImage());\n\n        this.activeFileConsumer = s -> EventQueue.invokeLater(() -> activeDownloadLabel.setText(s));\n\n        this.percentCompleteConsumer = value -> EventQueue.invokeLater(() -> {\n            progressBar.setValue(value);\n\n            if (value >= 100) {\n                final Thread thread = new Thread(() -> {\n                    try {\n                        Thread.sleep(3000);\n                        System.exit(BootLoader.REBOOT_EXIT);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                });\n\n                thread.start();\n            }\n        });\n\n        initComponents();\n    }\n\n    public Consumer<String> getActiveFileConsumer() {\n        return activeFileConsumer;\n    }\n\n    public IntConsumer getPercentCompleteConsumer() {\n        return percentCompleteConsumer;\n    }\n\n    private void initComponents() {\n        GridBagConstraints gridBagConstraints;\n\n        JScrollPane jScrollPane = new JScrollPane();\n        JTextArea messageArea = new JTextArea();\n\n        progressBar = new JProgressBar();\n        progressBar.setMinimum(0);\n        progressBar.setMaximum(100);\n        progressBar.setStringPainted(true);\n\n        JButton cancelButton = new JButton(ResourceUtils.getString(\"Button.Cancel\"));\n        cancelButton.addActionListener(evt -> cancelButtonActionPerformed());\n\n        activeDownloadLabel = new JLabel();\n        activeDownloadLabel.setFocusable(false);\n\n        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);\n        setTitle(\"Please Wait\");\n        setAlwaysOnTop(true);\n        setMinimumSize(new java.awt.Dimension(430, 220));\n        getContentPane().setLayout(new GridBagLayout());\n\n        jScrollPane.setBorder(null);\n\n        messageArea.setEditable(false);\n        messageArea.setColumns(20);\n        messageArea.setRows(3);\n        messageArea.setText(ResourceUtils.getString(\"Message.OpenJfxDownload\"));\n        messageArea.setWrapStyleWord(true);\n        messageArea.setFocusable(false);\n        messageArea.setOpaque(false);\n        messageArea.setRequestFocusEnabled(false);\n        jScrollPane.setViewportView(messageArea);\n\n        gridBagConstraints = new GridBagConstraints();\n        gridBagConstraints.gridx = 0;\n        gridBagConstraints.gridy = 0;\n        gridBagConstraints.fill = GridBagConstraints.BOTH;\n        gridBagConstraints.weightx = 1.0;\n        gridBagConstraints.weighty = 1.0;\n        gridBagConstraints.insets = new Insets(12, 12, 12, 12);\n        getContentPane().add(jScrollPane, gridBagConstraints);\n\n        gridBagConstraints = new GridBagConstraints();\n        gridBagConstraints.gridx = 0;\n        gridBagConstraints.gridy = 2;\n        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;\n        gridBagConstraints.ipadx = 677;\n        gridBagConstraints.insets = new Insets(6, 12, 12, 12);\n        getContentPane().add(progressBar, gridBagConstraints);\n\n        gridBagConstraints = new GridBagConstraints();\n        gridBagConstraints.gridx = 0;\n        gridBagConstraints.gridy = 3;\n        gridBagConstraints.anchor = GridBagConstraints.EAST;\n        gridBagConstraints.insets = new java.awt.Insets(12, 12, 12, 12);\n        getContentPane().add(cancelButton, gridBagConstraints);\n\n        gridBagConstraints = new GridBagConstraints();\n        gridBagConstraints.gridx = 0;\n        gridBagConstraints.gridy = 1;\n        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;\n        gridBagConstraints.insets = new Insets(12, 12, 6, 12);\n        getContentPane().add(activeDownloadLabel, gridBagConstraints);\n\n        pack();\n        setLocationRelativeTo(null);\n    }\n\n    private void cancelButtonActionPerformed() {\n        System.exit(progressBar.getValue() == 100 ? BootLoader.REBOOT_EXIT : -1);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/Options.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx;\n\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.scene.control.ButtonBar;\n\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.control.TimePeriodComboBox;\n\n/**\n * Manages application preferences.\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"SameParameterValue\")\npublic class Options {\n\n    private static final Preferences p = Preferences.userNodeForPackage(Options.class);\n\n    private static final String AUTO_PACK_TABLE = \"autoPackTables\";\n\n    private static final String CONFIRM_DELETE_REMINDER = \"confirmDeleteReminder\";\n\n    private static final String CONFIRM_DELETE_TRANSACTION = \"confirmDeleteTransaction\";\n\n    private static final String ACCOUNTING_TERMS = \"useAccountingTerms\";\n\n    private static final String REMEMBER_DATE = \"rememberDate\";\n\n    private static final String AUTO_COMPLETE = \"autoCompleteEnabled\";\n\n    private static final String CASE_SENSITIVE = \"autoCompleteIsCaseEnabled\";\n\n    private static final String CHECK_UPDATES = \"checkForUpdates\";\n\n    private static final String CONCATENATE_MEMOS = \"concatenateMemos\";\n\n    private static final String FUZZY_MATCH = \"autoCompleteFuzzyMatchEnabled\";\n\n    private static final String REMINDER_SNOOZE = \"reminderSnoozePeriod\";\n\n    private static final String OPEN_LAST = \"openLastEnabled\";\n\n    private static final String SELECT_ON_FOCUS = \"selectOnFocus\";\n\n    private static final String BUTTON_ORDER = \"buttonOrder\";\n\n    private static final String ANIMATIONS_ENABLED = \"animationsEnabled\";\n\n    private static final String RESTORE_LAST_TAB = \"restoreLastTab\";\n\n    private static final String REGEX_FOR_FILTERS = \"regexForFilters\";\n\n    private static final String GLOBAL_BAYES_ENABLED = \"globalBayesEnabled\";\n\n    private static final String LAST_FORMAT_CHANGE = \"lastFormatChange\";\n\n    private static final String RESTORE_REPORT_DATES = \"restoreReportDates\";\n\n    private static final int DEFAULT_SNOOZE = TimePeriodComboBox.getPeriods()[0];\n\n    private static final SimpleBooleanProperty useAccountingTerms;\n\n    private static final SimpleBooleanProperty checkForUpdates;\n\n    private static final SimpleBooleanProperty confirmDeleteTransaction;\n\n    private static final SimpleBooleanProperty confirmDeleteReminder;\n\n    private static final SimpleBooleanProperty rememberDate;\n\n    private static final SimpleBooleanProperty autoPackTablesEnabled;\n\n    private static final SimpleBooleanProperty autoCompleteEnabled;\n\n    private static final SimpleBooleanProperty autoCompleteCaseSensitiveEnabled;\n\n    private static final SimpleBooleanProperty autoCompleteFuzzyMatchEnabled;\n\n    private static final SimpleBooleanProperty selectOnFocusEnabled;\n\n    private static final SimpleBooleanProperty openLastEnabled;\n\n    private static final SimpleBooleanProperty animationsEnabled;\n\n    private static final SimpleBooleanProperty restoreLastRegisterTab;\n\n    private static final SimpleBooleanProperty concatenateMemos;\n\n    private static final SimpleBooleanProperty regexForFilters;\n\n    private static final SimpleBooleanProperty globalBayesEnabled;\n\n    private static final SimpleBooleanProperty restoreReportDates;\n\n    private static final SimpleIntegerProperty reminderSnoozePeriod;\n\n    private static final SimpleStringProperty buttonOrder;\n\n    private static final SimpleStringProperty fullNumericFormat;\n\n    private static final SimpleStringProperty shortNumericFormat;\n\n    private static final SimpleStringProperty shortDateFormat;\n\n    private static final ChangeListener<Boolean> booleanChangeListener;\n\n    private static final ChangeListener<Number> integerChangeListener;\n\n    static {\n        booleanChangeListener = (observable, oldValue, newValue) ->\n                p.putBoolean(((SimpleBooleanProperty)observable).getName(), newValue);\n\n        integerChangeListener = (observable, oldValue, newValue) ->\n                p.putInt(((SimpleIntegerProperty) observable).getName(), (Integer) newValue);\n\n        useAccountingTerms = createBooleanProperty(ACCOUNTING_TERMS, false);\n        checkForUpdates = createBooleanProperty(CHECK_UPDATES, true);\n        confirmDeleteTransaction = createBooleanProperty(CONFIRM_DELETE_TRANSACTION, true);\n        confirmDeleteReminder = createBooleanProperty(CONFIRM_DELETE_REMINDER, true);\n        rememberDate = createBooleanProperty(REMEMBER_DATE, true);\n        autoCompleteEnabled = createBooleanProperty(AUTO_COMPLETE, true);\n        autoCompleteCaseSensitiveEnabled = createBooleanProperty(CASE_SENSITIVE, false);\n        autoCompleteFuzzyMatchEnabled = createBooleanProperty(FUZZY_MATCH, false);\n        autoPackTablesEnabled = createBooleanProperty(AUTO_PACK_TABLE, true);\n        openLastEnabled = createBooleanProperty(OPEN_LAST, false);\n        selectOnFocusEnabled = createBooleanProperty(SELECT_ON_FOCUS, false);\n        concatenateMemos = createBooleanProperty(CONCATENATE_MEMOS, false);\n        animationsEnabled = createBooleanProperty(ANIMATIONS_ENABLED, true);\n        restoreLastRegisterTab = createBooleanProperty(RESTORE_LAST_TAB, true);\n        regexForFilters = createBooleanProperty(REGEX_FOR_FILTERS, false);\n        globalBayesEnabled = createBooleanProperty(GLOBAL_BAYES_ENABLED, false);\n        restoreReportDates = createBooleanProperty(RESTORE_REPORT_DATES, false);\n\n        reminderSnoozePeriod = createIntegerProperty(REMINDER_SNOOZE, DEFAULT_SNOOZE);\n\n        /* Zero value caused by a prior bug */\n        if (Options.reminderSnoozePeriodProperty().get() <= 0) {\n            Options.reminderSnoozePeriodProperty().setValue(DEFAULT_SNOOZE);\n        }\n\n        shortNumericFormat = createStringProperty(NumericFormats.getShortFormatPattern(), pattern -> {\n            NumericFormats.setShortFormatPattern(pattern);\n            updateLastFormatChange();\n        });\n\n        fullNumericFormat = createStringProperty(NumericFormats.getFullFormatPattern(), pattern -> {\n            NumericFormats.setFullFormatPattern(pattern);\n            updateLastFormatChange();\n        });\n\n        shortDateFormat = createStringProperty(DateUtils.getShortDatePattern(), pattern -> {\n            DateUtils.setShortDateFormatPattern(pattern);\n            updateLastFormatChange();\n        });\n\n        buttonOrder = createStringProperty(new ButtonBar().getButtonOrder(), s -> p.put(BUTTON_ORDER, s));\n\n        // Initialize with the current value if it's not been set before\n        if (getLastFormatChange() == 0) {\n            updateLastFormatChange();\n        }\n    }\n\n    private Options() {\n        // Utility class\n    }\n\n    private static SimpleBooleanProperty createBooleanProperty(final String name, final boolean defaultValue) {\n        final SimpleBooleanProperty property = new SimpleBooleanProperty(null, name, p.getBoolean(name, defaultValue));\n        property.addListener(booleanChangeListener);\n\n        return property;\n    }\n\n    private static SimpleIntegerProperty createIntegerProperty(final String name, final int defaultValue) {\n        final SimpleIntegerProperty property = new SimpleIntegerProperty(null, name, p.getInt(name, defaultValue));\n        property.addListener(integerChangeListener);\n\n        return property;\n    }\n\n    private static SimpleStringProperty createStringProperty(final String defaultValue, final Consumer<String> stringConsumer) {\n        final SimpleStringProperty property = new SimpleStringProperty(defaultValue);\n        property.addListener((observable, oldValue, newValue) -> stringConsumer.accept(newValue));\n        return property;\n    }\n\n    public static StringProperty fullNumericFormatProperty() {\n        return fullNumericFormat;\n    }\n\n    public static StringProperty shortNumericFormatProperty() {\n        return shortNumericFormat;\n    }\n\n    public static StringProperty shortDateFormatProperty() {\n        return shortDateFormat;\n    }\n\n    public static long getLastFormatChange() {\n        return p.getLong(LAST_FORMAT_CHANGE, 0);   // default should trigger an update\n    }\n\n    private static void updateLastFormatChange() {\n        p.putLong(LAST_FORMAT_CHANGE, System.currentTimeMillis());\n    }\n\n    /**\n     * Returns transaction deletion confirmation.\n     *\n     * @return true if confirm on transaction delete is enabled, false otherwise\n     */\n    public static BooleanProperty confirmOnTransactionDeleteProperty() {\n        return confirmDeleteTransaction;\n    }\n\n    public static BooleanProperty checkForUpdatesProperty() {\n        return checkForUpdates;\n    }\n\n    /**\n     * Returns reminder deletion confirmation.\n     *\n     * @return true if confirm on reminder delete is enabled, false otherwise\n     */\n    public static BooleanProperty confirmOnDeleteReminderProperty() {\n        return confirmDeleteReminder;\n    }\n\n    public static BooleanProperty concatenateMemosProperty() {\n        return concatenateMemos;\n    }\n\n    public static BooleanProperty useAccountingTermsProperty() {\n        return useAccountingTerms;\n    }\n\n    public static BooleanProperty globalBayesProperty() {\n        return globalBayesEnabled;\n    }\n\n    /**\n     * Determines if the last date used for a transaction is reset\n     * to the current date or remembered.\n     *\n     * @return true if the last date should be reused\n     */\n    public static BooleanProperty rememberLastDateProperty() {\n        return rememberDate;\n    }\n\n    /**\n     * Determines if the last date used for a report is restored or ignored.\n     *\n     * @return true if the last date should be used\n     */\n    public static BooleanProperty restoreReportDateProperty() {\n        return restoreReportDates;\n    }\n\n    /**\n     * Provides access to the enabled state of auto completion.\n     *\n     * @return {@code BooleanProperty} controlling enabled state\n     */\n    public static BooleanProperty useAutoCompleteProperty() {\n        return autoCompleteEnabled;\n    }\n\n    /**\n     * Provides access to the case sensitivity of auto completion.\n     *\n     * @return {@code BooleanProperty} controlling case sensitivity\n     */\n    public static BooleanProperty autoCompleteIsCaseSensitiveProperty() {\n        return autoCompleteCaseSensitiveEnabled;\n    }\n\n    /**\n     * Provides access to the case sensitivity of auto completion.\n     *\n     * @return {@code BooleanProperty} controlling case sensitivity\n     */\n    public static BooleanProperty autoPackTablesProperty() {\n        return autoPackTablesEnabled;\n    }\n\n    /** Provides access to the property controlling if fuzzy match is used for auto completion.\n     *\n     * @return {@code BooleanProperty} controlling fuzzy match\n     */\n    public static BooleanProperty useFuzzyMatchForAutoCompleteProperty() {\n        return autoCompleteFuzzyMatchEnabled;\n    }\n\n    /**\n     * Provides access to the property controlling the snooze time between reminder events.\n     *\n     * @return {@code IntegerProperty} controlling snooze time. Period is in milliseconds\n     */\n    public static IntegerProperty reminderSnoozePeriodProperty() {\n        return reminderSnoozePeriod;\n    }\n\n    /** Provides access to the property controlling if a text field automatically selects all text when\n     * it receives the focus.\n     *\n     * @return {@code BooleanProperty} controlling fuzzy match\n     */\n    public static BooleanProperty selectOnFocusProperty() {\n        return selectOnFocusEnabled;\n    }\n\n    /**\n     * Provides access to the open last file at startup property.\n     *\n     * @return {@code BooleanProperty} controlling open last file\n     */\n    public static BooleanProperty openLastProperty() {\n        return openLastEnabled;\n    }\n\n    /**\n     * Provides access to the enabled state for animations.\n     *\n     * @return {@code BooleanProperty} controlling open last file\n     */\n    public static BooleanProperty animationsEnabledProperty() {\n        return animationsEnabled;\n    }\n\n    /**\n     * Provides access to the restore last used tab property.\n     *\n     * @return {@code BooleanProperty} controlling open last file\n     */\n    public static BooleanProperty restoreLastTabProperty() {\n        return restoreLastRegisterTab;\n    }\n\n    /**\n     * Provides access to the property controlling use of regular expressions when applying filters.\n     *\n     * @return {@code BooleanProperty} controlling use of regex for filters/{@code Predicate}\n     */\n    public static BooleanProperty regexForFiltersProperty() {\n        return regexForFilters;\n    }\n\n    public static StringProperty buttonOrderProperty() {\n        return buttonOrder;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/StaticUIMethods.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx;\n\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.concurrent.Task;\nimport javafx.scene.control.ButtonType;\nimport javafx.scene.image.Image;\n\nimport jgnash.uifx.control.Alert;\nimport jgnash.uifx.control.ExceptionDialog;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.uifx.views.main.OpenDatabaseController;\nimport jgnash.util.Nullable;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Various static UI support methods.\n *\n * @author Craig Cavanaugh\n */\npublic class StaticUIMethods {\n\n    private static final String APP_ICON = \"/jgnash/resource/gnome-money.png\";\n\n    private static Image applicationImage;\n\n    private StaticUIMethods() {\n        // Utility class\n    }\n\n    public static void showOpenDialog() {\n        final FXMLUtils.Pair<OpenDatabaseController> pair\n                = FXMLUtils.load(OpenDatabaseController.class.getResource(\"OpenDatabaseForm.fxml\"),\n                ResourceUtils.getString(\"Title.Open\"));\n\n        pair.getStage().setResizable(true);\n        pair.getStage().show();\n\n        pair.getStage().setMaxHeight(pair.getStage().getHeight());\n\n        StageUtils.addBoundsListener(pair.getStage(), OpenDatabaseController.class, MainView.getPrimaryStage());\n    }\n\n    public static void displayError(final String message) {\n        final Alert alert = new Alert(Alert.AlertType.ERROR, message);\n\n        alert.setTitle(ResourceUtils.getString(\"Title.Error\"));\n        alert.initOwner(MainView.getPrimaryStage());\n\n        JavaFXUtils.runLater(alert::showAndWait);\n    }\n\n    public static void displayMessage(final String message) {\n        final Alert alert = new Alert(Alert.AlertType.INFORMATION, message);\n\n        alert.setTitle(ResourceUtils.getString(\"Title.Information\"));\n        alert.initOwner(MainView.getPrimaryStage());\n\n        JavaFXUtils.runLater(alert::showAndWait);\n    }\n\n    public static void displayWarning(final String message) {\n        final Alert alert = new Alert(Alert.AlertType.WARNING, message);\n\n        alert.setTitle(ResourceUtils.getString(\"Title.Warning\"));\n        alert.initOwner(MainView.getPrimaryStage());\n\n        JavaFXUtils.runLater(alert::showAndWait);\n    }\n\n    /**\n     * Displays a Yes and No dialog requesting confirmation.\n     *\n     * @param title   Dialog title\n     * @param message Dialog message\n     * @return {@code ButtonBar.ButtonData.YES} or {@code ButtonBar.ButtonData.NO}\n     */\n    public static ButtonType showConfirmationDialog(final String title, final String message) {\n        final Alert alert = new Alert(Alert.AlertType.YES_NO, message);\n\n        alert.setTitle(title);\n        alert.initOwner(MainView.getPrimaryStage());\n\n        final Optional<ButtonType> buttonType = alert.showAndWait();\n\n        return buttonType.orElse(ButtonType.NO);\n    }\n\n    public static void displayException(final Throwable exception) {\n        JavaFXUtils.runLater(() -> {\n            ExceptionDialog exceptionDialog = new ExceptionDialog(exception);\n            exceptionDialog.showAndWait();\n        });\n    }\n\n    public static void displayTaskProgress(final Task<?> task) {\n        MainView.getInstance().setBusy(task);\n    }\n\n\n    /**\n     * Returns the primary application icon.\n     *\n     * @return {@code} Image or {@code null} if not found\n     */\n    @Nullable\n    public static synchronized Image getApplicationIcon() {\n        if (applicationImage == null) {\n            try {\n                applicationImage = new Image(\n                        Objects.requireNonNull(StaticUIMethods.class.getResourceAsStream(APP_ICON)));\n            } catch (final Exception ex) {\n                Logger.getLogger(StaticUIMethods.class.getName()).log(Level.WARNING, ex.getLocalizedMessage(), ex);\n                applicationImage = null;\n            }\n\n        }\n\n        return applicationImage;\n    }\n\n    /**\n     * Handler for displaying uncaught exceptions.\n     */\n    public static class ExceptionHandler implements Thread.UncaughtExceptionHandler {\n\n        @Override\n        public void uncaughtException(final Thread t, final Throwable throwable) {\n            Logger.getLogger(ExceptionHandler.class.getName())\n                    .log(Level.SEVERE, throwable.getLocalizedMessage(), throwable);\n\n            displayException(throwable);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/about/AboutDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.about;\n\nimport java.util.Objects;\nimport java.util.Properties;\nimport java.util.ResourceBundle;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.input.Clipboard;\nimport javafx.scene.input.ClipboardContent;\nimport javafx.scene.web.WebView;\nimport javafx.stage.Stage;\n\nimport jgnash.resource.util.HTMLResource;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.FXMLUtils.Pair;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.TableViewManager;\nimport jgnash.util.NotNull;\n\n/**\n * About Dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class AboutDialogController {\n\n    private static final double FONT_SCALE = 0.8333;\n\n    private static final int MAX_HEIGHT = 490;\n\n    private static final String PREF_NODE = \"/jgnash/uifx/about/AboutDialogController\";\n\n    private static final double[] PREF_COLUMN_WEIGHTS = {0, 100};\n\n    private static final String DEFAULT = \"default\";\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private TabPane tabbedPane;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private TableViewManager<SystemProperty> tableViewManager;\n\n    @FXML\n    void initialize() {\n        tabbedPane.getTabs().addAll(\n                addHTMLTab(resources.getString(\"Tab.About\"), \"notice.html\"),\n                addHTMLTab(resources.getString(\"Tab.Credits\"), \"credits.html\"),\n                addHTMLTab(resources.getString(\"Tab.AppLicense\"), \"jgnash-license.html\"),\n                addHTMLTab(resources.getString(\"Tab.GPLLicense\"), \"gpl-license.html\"),\n                addHTMLTab(resources.getString(\"Tab.LGPLLicense\"), \"lgpl.html\"),\n                addHTMLTab(\"Apache License\", \"apache-license.html\"),\n                addHTMLTab(\"XStream License\", \"xstream-license.html\"),\n                getSystemPropertiesTab());\n\n    }\n\n    private static Tab addHTMLTab(final String name, final String resource) {\n        final WebView webView = new WebView();\n\n        webView.setFontScale(FONT_SCALE);\n        webView.setMaxHeight(MAX_HEIGHT);\n\n        // be paranoid, protect against external scripts\n        webView.getEngine().setJavaScriptEnabled(false);\n        webView.getEngine().load(HTMLResource.getURL(resource).toExternalForm());\n\n        return new Tab(name, webView);\n    }\n\n    private Tab getSystemPropertiesTab() {\n\n        final ObservableList<SystemProperty> propertiesList = FXCollections.observableArrayList();\n\n        final Properties properties = System.getProperties();\n\n        propertiesList.addAll(properties.stringPropertyNames().stream().map(prop ->\n                new SystemProperty(prop, properties.getProperty(prop))).collect(Collectors.toList()));\n\n        FXCollections.sort(propertiesList);\n\n\n\n        final TableColumn<SystemProperty, String> keyCol = new TableColumn<>(resources.getString(\"Column.PropName\"));\n        keyCol.setCellValueFactory(param -> param.getValue().keyProperty());\n\n        final TableColumn<SystemProperty, String> valueCol = new TableColumn<>(resources.getString(\"Column.PropVal\"));\n        valueCol.setCellValueFactory(param -> param.getValue().valueProperty());\n\n        final TableView<SystemProperty> tableView = new TableView<>(propertiesList);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        final ObservableList<TableColumn<SystemProperty, ?>>tableViewColumns = tableView.getColumns();\n\n        tableViewColumns.add(keyCol);\n        tableViewColumns.add(valueCol);\n\n        final ContextMenu menu = new ContextMenu();\n        final MenuItem copyMenuItem = new MenuItem(resources.getString(\"Menu.Copy.Name\"));\n\n        copyMenuItem.setOnAction(event -> dumpPropertiesToClipboard(tableView));\n\n        menu.getItems().add(copyMenuItem);\n        tableView.setContextMenu(menu);\n\n        tableViewManager = new TableViewManager<>(tableView, PREF_NODE);\n        tableViewManager.setColumnWeightFactory(column -> PREF_COLUMN_WEIGHTS[column]);\n        tableViewManager.setPreferenceKeyFactory(() -> DEFAULT);\n\n        JavaFXUtils.runLater(tableViewManager::packTable);\n\n        return new Tab(ResourceUtils.getString(\"Tab.SysInfo\"), tableView);\n    }\n\n    private static void dumpPropertiesToClipboard(final TableView<SystemProperty> tableView) {\n        final StringBuilder buffer = new StringBuilder();\n\n        tableView.getSelectionModel().getSelectedItems().stream().filter(systemProperty ->\n                systemProperty.keyProperty().get() != null).forEach(systemProperty -> {\n            buffer.append(systemProperty.keyProperty().get());\n            buffer.append(\"\\t\");\n            if (systemProperty.valueProperty().get() != null) {\n                buffer.append(systemProperty.valueProperty().get());\n            }\n            buffer.append(\"\\n\");\n        });\n\n        final ClipboardContent content = new ClipboardContent();\n        content.putString(buffer.toString());\n\n        Clipboard.getSystemClipboard().setContent(content);\n    }\n\n    public static void showAndWait() {\n        JavaFXUtils.runLater(() -> {   // push to EDT to avoid race when loading the html files\n            final Pair<AboutDialogController> pair = FXMLUtils.load(AboutDialogController.class.getResource(\"AboutDialog.fxml\"),\n                    ResourceUtils.getString(\"Title.About\"));\n\n            pair.getStage().setResizable(false);\n            pair.getStage().show();\n        });\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) tabbedPane.getScene().getWindow()).close();\n    }\n\n    private static class SystemProperty implements Comparable<SystemProperty> {\n        private final StringProperty key;\n        private final StringProperty value;\n\n        private SystemProperty(final String name, final String value) {\n            this.key = new SimpleStringProperty(name);\n            this.value = new SimpleStringProperty(value);\n        }\n\n        StringProperty keyProperty() {\n            return key;\n        }\n\n        StringProperty valueProperty() {\n            return value;\n        }\n\n        @Override\n        public int compareTo(@NotNull final SystemProperty o) {\n            return keyProperty().get().compareTo(o.keyProperty().get());\n        }\n\n        @Override\n        public boolean equals(final Object obj) {\n            if (this == obj) {\n                return true;\n            }\n\n            if (obj == null || getClass() != obj.getClass()) {\n                return false;\n            }\n\n            final SystemProperty that = (SystemProperty) obj;\n\n            return Objects.equals(key, that.key) && Objects.equals(value, that.value);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(key, value);\n        }\n    }\n}\n\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/DatabasePathAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Utility class request database path from the user.\n *\n * @author Craig Cavanaugh\n */\npublic class DatabasePathAction {\n\n    private static final String LAST_DIR = \"LastDir\";\n\n    private DatabasePathAction() {\n        // utility class\n    }\n\n    public static File getFileToSave() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.NewFile\"));\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(DatabasePathAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n        }\n\n        return file;\n    }\n\n    public static File getFileToOpen() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.Open\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(DatabasePathAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n        }\n\n        return file;\n    }\n\n    private static FileChooser configureFileChooser() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final Preferences pref = Preferences.userNodeForPackage(DatabasePathAction.class);\n\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        final List<String> types = new ArrayList<>();\n\n        for (final DataStoreType dataStoreType : DataStoreType.values()) {\n            types.add(\"*\" + dataStoreType.getDataStore().getFileExt());\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.jGnashFiles\"), types)\n        );\n\n        for (final DataStoreType dataStoreType : DataStoreType.values()) {\n            fileChooser.getExtensionFilters().addAll(\n                    new FileChooser.ExtensionFilter(dataStoreType.toString(), \"*\" + dataStoreType.getDataStore().getFileExt())\n            );\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"All Files\", \"*.*\")\n        );\n\n        return fileChooser;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/DefaultCurrencyAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.concurrent.Task;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.ChoiceDialog;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * UI Action to change the default currency.\n *\n * @author Craig Cavanaugh\n */\npublic class DefaultCurrencyAction {\n\n    public static void showAndWait() {\n\n        final Task<List<CurrencyNode>> task = new Task<>() {\n            final ResourceBundle resources = ResourceUtils.getBundle();\n\n            private List<CurrencyNode> currencyNodeList;\n\n            @Override\n            protected List<CurrencyNode> call() {\n\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                currencyNodeList = engine.getCurrencies();\n\n                return currencyNodeList;\n            }\n\n            @Override\n            protected void succeeded() {\n                super.succeeded();\n\n                JavaFXUtils.runLater(() -> {\n\n                    final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                    Objects.requireNonNull(engine);\n\n                    final ChoiceDialog<CurrencyNode> dialog = new ChoiceDialog<>(engine.getDefaultCurrency(), currencyNodeList);\n                    dialog.setTitle(resources.getString(\"Title.SelDefCurr\"));\n                    dialog.setContentText(resources.getString(\"Title.SelDefCurr\"));\n\n                    final Optional<CurrencyNode> optional = dialog.showAndWait();\n\n                    optional.ifPresent(currencyNode -> {\n                        engine.setDefaultCurrency(currencyNode);\n\n                        JavaFXUtils.runLater(() -> StaticUIMethods.displayMessage(resources.getString(\"Message.CurrChange\")\n                                + \" \" + engine.getDefaultCurrency().getSymbol()));\n\n                    });\n                });\n            }\n        };\n\n        new Thread(task).start();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/DefaultLocaleAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Locale;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.concurrent.Task;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.ChoiceDialog;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.LocaleObject;\n\n/**\n * UI Action to change the default locale.\n *\n * @author Craig Cavanaugh\n */\npublic class DefaultLocaleAction {\n\n    public static void showAndWait() {\n\n        final Task<Collection<LocaleObject>> task = new Task<>() {\n            final ResourceBundle resources = ResourceUtils.getBundle();\n\n            private Collection<LocaleObject> localeObjects;\n\n            @Override\n            protected Collection<LocaleObject> call() {\n                localeObjects = new ArrayList<>(LocaleObject.getLocaleObjects());\n                return localeObjects;\n            }\n\n            @Override\n            protected void succeeded() {\n                super.succeeded();\n\n                JavaFXUtils.runLater(() -> {\n                    final ChoiceDialog<LocaleObject> dialog = new ChoiceDialog<>(new LocaleObject(Locale.getDefault()), localeObjects);\n                    dialog.setTitle(resources.getString(\"Title.SelDefLocale\"));\n                    dialog.setContentText(resources.getString(\"Title.SelDefLocale\"));\n\n                    final Optional<LocaleObject> optional = dialog.showAndWait();\n\n                    optional.ifPresent(localeObject -> {\n                        ResourceUtils.setLocale(localeObject.getLocale());\n\n                        JavaFXUtils.runLater(() -> StaticUIMethods.displayMessage(localeObject + \"\\n\" +\n                                resources.getString(\"Message.RestartLocale\")));\n\n                    });\n                });\n            }\n        };\n\n        new Thread(task).start();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/ExecuteJavaScriptAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javafx.stage.FileChooser;\n\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * Utility class to run a javascript file.\n *\n * @author Craig Cavanaugh\n */\npublic class ExecuteJavaScriptAction {\n\n    private static final String LAST_DIR = \"javaScriptDir\";\n\n    private ExecuteJavaScriptAction() {\n        // Utility class\n    }\n\n    public static void showAndWait() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(ExecuteJavaScriptAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            JavaFXUtils.runLater(() -> {\n                try (final Reader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {\n                    new ScriptEngineManager().getEngineByName(\"nashorn\").eval(reader);\n                } catch (IOException | ScriptException ex) {\n                    Logger.getLogger(ExecuteJavaScriptAction.class.getName()).log(Level.SEVERE, ex.toString(), ex);\n                }\n            });\n        }\n    }\n\n    private static FileChooser configureFileChooser() {\n        final Preferences pref = Preferences.userNodeForPackage(ExecuteJavaScriptAction.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"JavaScript Files\", \"*.js\")\n        );\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"All Files\", \"*.*\")\n        );\n\n        return fileChooser;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/ExportAccountsAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.AccountTreeXMLFactory;\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.FileUtils;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * UI Action to export the current account tree.\n *\n * @author Craig Cavanaugh\n */\npublic class ExportAccountsAction {\n\n    private static final String LAST_DIR = \"exportDir\";\n\n    private ExportAccountsAction() {\n        // Utility class\n    }\n\n    public static void showAndWait() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(ExportAccountsAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            final ExportTask exportTask =\n                    new ExportTask(Paths.get(FileUtils.stripFileExtension(file.getAbsolutePath()) + \".xml\"));\n\n            new Thread(exportTask).start();\n\n            MainView.getInstance().setBusy(exportTask);\n        }\n    }\n\n    private static FileChooser configureFileChooser() {\n        final Preferences pref = Preferences.userNodeForPackage(ExportAccountsAction.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\n                        ResourceUtils.getString(\"Label.XMLFiles\") + \" (*.xml)\", \"*.xml\", \"*.XML\")\n        );\n\n        return fileChooser;\n    }\n\n    private static class ExportTask extends Task<Void> {\n        private final Path file;\n\n        ExportTask(final Path file) {\n            this.file = file;\n        }\n\n        @Override\n        protected Void call() {\n            updateMessage(ResourceUtils.getString(\"Message.PleaseWait\"));\n            updateProgress(-1, Long.MAX_VALUE);\n\n            AccountTreeXMLFactory.exportAccountTree(EngineFactory.getEngine(EngineFactory.DEFAULT), file);\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/ImportAccountsAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.AccountTreeXMLFactory;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.RootAccount;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.FileUtils;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * UI Action to import a tree of accounts.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportAccountsAction {\n\n    private static final String LAST_DIR = \"importDir\";\n\n    private ImportAccountsAction() {\n        // Utility class\n    }\n\n    public static void showAndWait() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(ImportAccountsAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            final ImportTask importTask =\n                    new ImportTask(Paths.get(FileUtils.stripFileExtension(file.getAbsolutePath()) + \".xml\"));\n\n            new Thread(importTask).start();\n\n            MainView.getInstance().setBusy(importTask);\n        }\n    }\n\n    private static FileChooser configureFileChooser() {\n        final Preferences pref = Preferences.userNodeForPackage(ImportAccountsAction.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\n                        ResourceUtils.getString(\"Label.XMLFiles\") + \" (*.xml)\", \"*.xml\", \"*.XML\")\n        );\n\n        return fileChooser;\n    }\n\n    private static class ImportTask extends Task<Void> {\n        private final Path file;\n\n        ImportTask(final Path file) {\n            this.file = file;\n        }\n\n        @Override\n        protected Void call() {\n            updateMessage(ResourceUtils.getString(\"Message.ImportWait\"));\n            updateProgress(-1, Long.MAX_VALUE);\n\n            final RootAccount root = AccountTreeXMLFactory.loadAccountTree(file);\n\n            if (root != null) {\n                AccountTreeXMLFactory.mergeAccountTree(EngineFactory.getEngine(EngineFactory.DEFAULT), root);\n            }\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/ImportOfxAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.io.File;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.stage.FileChooser;\n\nimport jgnash.convert.importat.GenericImport;\nimport jgnash.convert.importat.ofx.OfxBank;\nimport jgnash.convert.importat.ofx.OfxImport;\nimport jgnash.convert.importat.ofx.OfxV2Parser;\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.wizard.WizardDialogController;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.uifx.wizard.imports.ImportWizard;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Utility class to import an OFX file.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportOfxAction {\n\n    private static final String LAST_DIR = \"importDir\";\n\n    private ImportOfxAction() {\n        // Utility class\n    }\n\n    public static void showAndWait() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(ImportOfxAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            new Thread(new ImportTask(file)).start();\n        }\n    }\n\n    private static FileChooser configureFileChooser() {\n        final Preferences pref = Preferences.userNodeForPackage(ImportOfxAction.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File lastDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        if (lastDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(lastDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"OFX Files (*.ofx,*.qfx)\", \"*.ofx\", \"*.qfx\", \"*.OFX\", \"*.QFX\"),\n                new FileChooser.ExtensionFilter(\"All Files (*.*)\", \"*.*\")\n        );\n\n        return fileChooser;\n    }\n\n    private static class ImportTask extends Task<OfxBank> {\n\n        private final File file;\n\n        private Account match = null;\n\n        ImportTask(final File file) {\n            this.file = file;\n\n            setOnSucceeded(event -> onSuccess());\n        }\n\n        @Override\n        protected OfxBank call() throws Exception {\n            final OfxBank ofxBank = OfxV2Parser.parse(file.toPath());\n\n            /* Preset the best match for the downloaded account */\n            final String accountNumber = ofxBank.accountId;\n\n            if (accountNumber != null && !accountNumber.isEmpty()) {\n                match = OfxImport.matchAccount(ofxBank);\n            }\n\n            return ofxBank;\n        }\n\n        private void onSuccess() {\n            final OfxBank ofxBank = getValue();\n\n            final ImportWizard importWizard = new ImportWizard();\n\n            WizardDialogController<ImportWizard.Settings> wizardDialogController\n                    = importWizard.wizardControllerProperty().get();\n\n            // Set the bank match first for a better work flow\n            if (match != null) {\n                wizardDialogController.setSetting(ImportWizard.Settings.ACCOUNT, match);\n            }\n\n            wizardDialogController.setSetting(ImportWizard.Settings.BANK, ofxBank);\n\n            importWizard.showAndWait();\n\n            if (wizardDialogController.validProperty().get()) {\n                final Account account = (Account) wizardDialogController.getSetting(ImportWizard.Settings.ACCOUNT);\n\n                // import threads in the background\n                final ImportTransactionsTask importTransactionsTask =\n                        new ImportTransactionsTask(ofxBank, account);\n\n                new Thread(importTransactionsTask).start();\n\n                StaticUIMethods.displayTaskProgress(importTransactionsTask);\n            }\n        }\n    }\n\n    private static class ImportTransactionsTask extends Task<Void> {\n\n        private final OfxBank bank;\n        private final Account account;\n\n        ImportTransactionsTask(final OfxBank bank, final Account account) {\n            this.bank = bank;\n            this.account = account;\n        }\n\n        @Override\n        public Void call() {\n            updateMessage(ResourceUtils.getString(\"Message.PleaseWait\"));\n            updateProgress(-1, Long.MAX_VALUE);\n\n            String accountNumber = bank.accountId;\n\n            /* set the account number if not a match */\n            if (accountNumber != null && !accountNumber.equals(account.getAccountNumber())) {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                engine.setAccountNumber(account, accountNumber);\n            }\n\n            // Import or update securities that were found\n            if (bank.getSecurityList().size() > 0) {\n                GenericImport.importSecurities(bank.getSecurityList(), account.getCurrencyNode());\n            }\n\n            // Import the transactions\n            try {\n                OfxImport.importTransactions(bank, account);\n            } catch (final Exception e) {\n                Logger.getLogger(ImportOfxAction.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/actions/ImportQifAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.actions;\n\nimport java.io.File;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.stage.FileChooser;\n\nimport jgnash.convert.importat.GenericImport;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.convert.importat.qif.QifAccount;\nimport jgnash.convert.importat.qif.QifImport;\nimport jgnash.convert.importat.qif.QifParser;\nimport jgnash.convert.importat.qif.QifUtils;\nimport jgnash.engine.Account;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.wizard.WizardDialogController;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.uifx.wizard.imports.ImportWizard;\n\n/**\n * Utility class to import an OFX file.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportQifAction {\n\n    private static final String LAST_DIR = \"importDir\";\n\n    private ImportQifAction() {\n        // Utility class\n    }\n\n    public static void showAndWait() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(ImportQifAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            new Thread(new ImportTask(file)).start();\n        }\n    }\n\n    private static FileChooser configureFileChooser() {\n        final Preferences pref = Preferences.userNodeForPackage(ImportQifAction.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"Qif Files (*.qif)\", \"*.qif\")\n        );\n\n        return fileChooser;\n    }\n\n    private static class ImportTask extends Task<QifImport> {\n\n        private final File file;\n\n        ImportTask(final File file) {\n            this.file = file;\n            setOnSucceeded(event -> onSuccess());\n        }\n\n        @Override\n        protected QifImport call() {\n            if (QifUtils.isFullFile(file)) {\n                JavaFXUtils.runLater(() -> StaticUIMethods.displayError(\"Only bank statement based QIF file are \" +\n                                                                                \"supported at this time\"));\n                cancel();\n                return null;\n            }\n\n            final QifImport qifImport = new QifImport();\n\n            if (!qifImport.doPartialParse(file)) {\n                JavaFXUtils.runLater(() -> StaticUIMethods.displayError(\n                        ResourceUtils.getString(\"Message.Error.ParseTransactions\")));\n\n                cancel();\n                return null;\n            }\n\n            qifImport.dumpStats();\n\n            if (qifImport.getParser().accountList.isEmpty()) {\n                JavaFXUtils.runLater(() -> StaticUIMethods.displayError(\n                        ResourceUtils.getString(\"Message.Error.ParseTransactions\")));\n                cancel();\n                return null;\n            }\n\n            return qifImport;\n        }\n\n        private void onSuccess() {\n            final QifImport qifImport = getValue();\n            final QifParser parser = qifImport.getParser();\n\n            final ImportWizard importWizard = new ImportWizard();\n\n            final WizardDialogController<ImportWizard.Settings> wizardDialogController\n                    = importWizard.wizardControllerProperty().get();\n\n            importWizard.dateFormatSelectionEnabled().set(true);\n\n            final QifAccount qAccount = parser.getBank();\n\n            wizardDialogController.setSetting(ImportWizard.Settings.BANK, qAccount);\n\n            importWizard.showAndWait();\n\n            if (wizardDialogController.validProperty().get()) {\n                final Account account = (Account) wizardDialogController.getSetting(ImportWizard.Settings.ACCOUNT);\n\n                @SuppressWarnings(\"unchecked\") final List<ImportTransaction> transactions = (List<ImportTransaction>) wizardDialogController.getSetting(ImportWizard.Settings.TRANSACTIONS);\n\n                // import threads in the background\n                ImportTransactionsTask importTransactionsTask = new ImportTransactionsTask(account, transactions);\n\n                new Thread(importTransactionsTask).start();\n\n                StaticUIMethods.displayTaskProgress(importTransactionsTask);\n            }\n        }\n    }\n\n    private static class ImportTransactionsTask extends Task<Void> {\n\n        private final Account account;\n        private final List<ImportTransaction> transactions;\n\n        ImportTransactionsTask(final Account account, final List<ImportTransaction> transactions) {\n            this.account = account;\n            this.transactions = transactions;\n        }\n\n        @Override\n        public Void call() {\n            updateMessage(ResourceUtils.getString(\"Message.PleaseWait\"));\n            updateProgress(-1, Long.MAX_VALUE);\n\n            /* Import the transactions */\n            GenericImport.importTransactions(transactions, account);\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/AbstractAccountTreeController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.Objects;\nimport java.util.TreeSet;\n\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableSet;\nimport javafx.collections.SetChangeListener;\nimport javafx.scene.control.TreeItem;\nimport javafx.scene.control.TreeView;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.TreeSearch;\nimport jgnash.util.Nullable;\n\n/**\n * Abstract Controller handling a {@code TreeView} of {@code Account}s.\n *\n * @author Craig Cavanaugh\n */\npublic abstract class AbstractAccountTreeController implements MessageListener {\n\n    private final ReadOnlyObjectWrapper<Account> selectedAccount = new ReadOnlyObjectWrapper<>();\n\n    protected abstract TreeView<Account> getTreeView();\n\n    /**\n     * Determines account visibility.\n     *\n     * @param account {@code Account} to determine visibility based on filter state\n     * @return {@code true} if the {@code Account} should be visible\n     */\n    protected abstract boolean isAccountVisible(Account account);\n\n    protected abstract boolean isAccountSelectable(Account account);\n\n    private final ObservableSet<Account> filteredAccounts = FXCollections.observableSet(new TreeSet<>());\n\n    /**\n     * Adds accounts to be excluded from the list of selectable accounts.\n     *\n     * @param filteredAccountsList collection of {@code Account} that should be excluded regardless\n     *                             of {@code isAccountVisible()}\n     */\n    public void addExcludeAccounts(@Nullable final Account... filteredAccountsList) {\n        Objects.requireNonNull(filteredAccounts);\n\n        for (final Account account: filteredAccountsList != null ? filteredAccountsList : new Account[0]) {\n            if (account != null) {\n                filteredAccounts.add(account);\n            }\n        }\n    }\n\n    public void initialize() {\n        getTreeView().setShowRoot(false);\n\n        loadAccountTree();\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM, MessageChannel.ACCOUNT);\n\n        getTreeView().getSelectionModel().selectedItemProperty()\n                .addListener((observable, oldValue, newValue) -> {\n                    if (newValue != null) {\n                        if (isAccountSelectable(newValue.getValue())) {\n                            selectedAccount.set(newValue.getValue());\n                        }\n                    }\n                });\n\n        filteredAccounts.addListener((SetChangeListener<Account>) change -> reload());\n    }\n\n    public ReadOnlyObjectProperty<Account> getSelectedAccountProperty() {\n        return selectedAccount.getReadOnlyProperty();\n    }\n\n    public void setSelectedAccount(final Account account) {\n        final TreeItem<Account> treeItem = TreeSearch.findTreeItem(getTreeView().getRoot(), account);\n\n        if (treeItem != null) {\n            JavaFXUtils.runLater(() -> getTreeView().getSelectionModel().select(treeItem));\n        }\n    }\n\n    public void reload() {\n        JavaFXUtils.runLater(this::loadAccountTree);\n    }\n\n    private void loadAccountTree() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            final TreeItem<Account> root = new TreeItem<>(engine.getRootAccount());\n            root.setExpanded(true);\n\n            getTreeView().setRoot(root);\n            loadChildren(root);\n        } else {\n            getTreeView().setRoot(null);\n        }\n    }\n\n    private synchronized void loadChildren(final TreeItem<Account> parentItem) {\n        final Account parent = parentItem.getValue();\n\n        parent.getChildren(Comparators.getAccountByCode()).stream().filter(child ->\n                !filteredAccounts.contains(child) && isAccountVisible(child)).forEach(child ->\n        {\n            final TreeItem<Account> childItem = new TreeItem<>(child);\n            childItem.setExpanded(true);\n            parentItem.getChildren().add(childItem);\n\n            if (child.getChildCount() > 0) {\n                loadChildren(childItem);\n            }\n        });\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        switch (event.getEvent()) {\n            case ACCOUNT_ADD:\n            case ACCOUNT_MODIFY:\n            case ACCOUNT_REMOVE:\n                reload();\n                break;\n            case FILE_CLOSING:\n                JavaFXUtils.runLater(() -> getTreeView().setRoot(null));   // dump account references immediately\n                MessageBus.getInstance().unregisterListener(this, MessageChannel.SYSTEM, MessageChannel.ACCOUNT);\n                break;\n            default:\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/AccountComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Predicate;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.SimpleListProperty;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.FilteredList;\nimport javafx.event.EventHandler;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.cell.ComboBoxListCell;\nimport javafx.scene.input.KeyEvent;\nimport javafx.util.StringConverter;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.NotNull;\n\n/**\n * ComboBox of available accounts.  A Predicate for allowed accounts may be specified.\n * <p>\n * The combo will disable itself if it is empty after filtering.\n *\n * <a href=\"https://bugs.openjdk.java.net/browse/JDK-8129123\">Bug JDK-8129123</a>\n *\n * @author Craig Cavanaugh\n */\npublic class AccountComboBox extends ComboBox<Account> implements MessageListener {\n\n    private final FilteredList<Account> filteredList;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final SimpleListProperty<Account> listProperty;\n\n    private final ObservableList<Account> items;\n\n    private ListView<Account> listView;\n\n    public AccountComboBox() {\n\n        items = getItems();\n\n        // By default only visible accounts that are not locked or placeholders are shown\n        filteredList = new FilteredList<>(items, getDefaultPredicate());\n\n        listProperty = new SimpleListProperty<>(filteredList);\n\n        setItems(filteredList);\n\n        loadAccounts();\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.ACCOUNT, MessageChannel.SYSTEM);\n\n        setOnMouseClicked(event -> forceScrollSelectionBugWorkAround());\n\n        setOnKeyTyped(new KeyHandler());\n\n        final StringConverter<Account> accountStringConverter = new StringConverter<>() {\n            @Override\n            public String toString(final Account account) {\n                return account == null ? null : account.getPathName();\n            }\n\n            @Override\n            public Account fromString(final String string) {\n                for (final Account account : filteredList) {\n                    if (account.getPathName().equals(string)) {\n                        return account;\n                    }\n                }\n                return null;\n            }\n        };\n\n        // cell factory for capturing the ListView needed for addressing JDK Bug JDK-8129123\n        setCellFactory(param -> {\n            final ComboBoxListCell<Account> comboBoxListCell = new ComboBoxListCell<>();\n\n            comboBoxListCell.listViewProperty().addListener((observable, oldValue, newValue) -> {\n                if (newValue != null) {\n                    listView = newValue;\n                }\n            });\n\n            comboBoxListCell.setConverter(accountStringConverter);\n\n            return comboBoxListCell;\n        });\n\n        // display the full account path instead of the name\n        setConverter(accountStringConverter);\n\n        // disable if empty\n        disableProperty().bind(Bindings.equal(listProperty.sizeProperty(), 0));\n    }\n\n    /**\n     * Returns the default Predicate used to determine which Accounts are displayed.\n     * <p>\n     * The default is only visible accounts that are not locked or placeholders are shown.\n     *\n     * @return default Account Predicate\n     */\n    public static Predicate<Account> getDefaultPredicate() {\n        return account -> account.isVisible() && !account.isLocked() && !account.isPlaceHolder();\n    }\n\n    /**\n     * Returns a Predicate used to display all accounts.\n     *\n     * @return Account Predicate\n     */\n    public static Predicate<Account> getShowAllPredicate() {\n        return account -> true;\n    }\n\n    // TODO: JDK Bug JDK-8129123 https://bugs.openjdk.java.net/browse/JDK-8129123\n    private void forceScrollSelectionBugWorkAround() {\n        if (listView != null) {\n            listView.scrollTo(getSelectionModel().getSelectedIndex());\n        }\n    }\n\n    public ObservableList<Account> getUnfilteredItems() {\n        return items;\n    }\n\n    public void setPredicate(final Predicate<Account> predicate) {\n        filteredList.setPredicate(predicate);\n\n        // force an update if the filtered list does not contain the current value\n        if (!filteredList.contains(getValue())) {\n            selectDefaultAccount();\n        }\n    }\n\n    private void selectDefaultAccount() {\n        // Set a default account, must use the filtered list because that is what is visible\n        if (filteredList.size() > 0) {\n            JavaFXUtils.runLater(() -> setValue(filteredList.get(0)));\n        }\n    }\n\n    /**\n     * A decorator around {@link ComboBox#setValue(Object)} that ensures only Accounts available within this ComboBox\n     * are set.\n     *\n     * @param value Account to set\n     */\n    public final void setAccountValue(final Account value) {\n        if (value == null || filteredList.contains(value)) {\n            setValue(value);\n        }\n    }\n\n    private void loadAccounts(@NotNull final List<Account> accounts) {\n        accounts.forEach(account -> {\n            getUnfilteredItems().add(account);\n\n            if (account.getChildCount() > 0) {\n                loadAccounts(account.getChildren(Comparators.getAccountByCode()));\n            }\n        });\n    }\n\n    private void loadAccounts() {\n        getUnfilteredItems().clear();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        loadAccounts(engine.getRootAccount().getChildren(Comparators.getAccountByCode()));\n\n        selectDefaultAccount();\n    }\n\n    /**\n     * Deterministic removal of an account\n     *\n     * @param account Account to remove\n     */\n    private void removeAccount(final Account account) {\n        final int oldIndex = items.indexOf(account);\n\n        items.removeAll(account);\n\n        if (oldIndex >= 0) {\n            setAccountValue(items.get(oldIndex));\n        } else if (items.size() > 0) {\n            setAccountValue(items.get(0));\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n\n        // unregister immediately\n        if (event.getEvent() == ChannelEvent.FILE_CLOSING) {\n            MessageBus.getInstance().unregisterListener(this, MessageChannel.ACCOUNT, MessageChannel.SYSTEM);\n        }\n\n        JavaFXUtils.runLater(() -> {\n            switch (event.getEvent()) {\n                case FILE_CLOSING:\n                    items.clear();\n                    break;\n                case ACCOUNT_REMOVE:\n                    removeAccount(event.getObject(MessageProperty.ACCOUNT));\n                    break;\n                case ACCOUNT_ADD:\n                case ACCOUNT_MODIFY:\n                    loadAccounts();\n                    break;\n                default:\n                    break;\n            }\n        });\n    }\n\n    private class KeyHandler implements EventHandler<KeyEvent> {\n\n        @Override\n        public void handle(final KeyEvent event) {\n            int current = getSelectionModel().getSelectedIndex();\n\n            if (current < 0 || current > filteredList.size()) {\n                current = 0;\n            }\n\n            int loop = current;\n\n            do {\n                loop++; //move to next item\n                if (loop > filteredList.size() - 1) {\n                    loop = 0;\n                }\n\n                final Account item = filteredList.get(loop);\n                if (item.toString().toUpperCase().startsWith(event.getCharacter().toUpperCase())) {\n                    getSelectionModel().select(item);\n                    break;\n                }\n            } while (loop != current);\n\n            forceScrollSelectionBugWorkAround();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/Alert.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ButtonType;\nimport javafx.scene.control.Label;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\nimport javafx.stage.Window;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.util.NotNull;\n\n/**\n * A Better behaved Alert class.\n *\n * @author Craig Cavanaugh\n */\npublic class Alert {\n\n    static final int HEIGHT_MULTIPLIER = 3;\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Label message;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    private ButtonType buttonType = ButtonType.CANCEL;  // default is cancelled\n\n    public enum AlertType {\n        ERROR,\n        WARNING,\n        INFORMATION,\n        YES_NO\n    }\n\n    private final Stage dialog;\n\n    public Alert(@NotNull final AlertType alertType, final String contentText) {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        dialog = FXMLUtils.loadFXML(this, \"AlertDialog.fxml\", resources);\n\n        setContentText(contentText);\n\n        switch (alertType) {\n            case ERROR:\n                setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.EXCLAMATION_TRIANGLE,\n                        ThemeManager.getBaseTextHeight() * HEIGHT_MULTIPLIER, Color.DARKRED));\n                setButtons(new ButtonType(resources.getString(\"Button.Close\"), ButtonBar.ButtonData.CANCEL_CLOSE));\n                break;\n            case WARNING:\n                setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.EXCLAMATION_CIRCLE,\n                        ThemeManager.getBaseTextHeight() * HEIGHT_MULTIPLIER, Color.DARKGOLDENROD));\n                setButtons(new ButtonType(resources.getString(\"Button.Close\"), ButtonBar.ButtonData.CANCEL_CLOSE));\n                break;\n            case INFORMATION:\n                setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.INFO_CIRCLE,\n                        ThemeManager.getBaseTextHeight() * HEIGHT_MULTIPLIER, Color.DARKGOLDENROD));\n                setButtons(new ButtonType(resources.getString(\"Button.Close\"), ButtonBar.ButtonData.CANCEL_CLOSE));\n                break;\n            case YES_NO:\n                setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.QUESTION_CIRCLE,\n                        ThemeManager.getBaseTextHeight() * HEIGHT_MULTIPLIER));\n                ButtonType buttonTypeYes = new ButtonType(resources.getString(\"Button.Yes\"), ButtonBar.ButtonData.YES);\n                ButtonType buttonTypeNo = new ButtonType(resources.getString(\"Button.No\"), ButtonBar.ButtonData.NO);\n                setButtons(buttonTypeYes, buttonTypeNo);\n                break;\n            default:\n        }\n    }\n\n    public void setTitle(final String title) {\n        dialog.setTitle(title);\n    }\n\n    public void initOwner(final Window window) {\n        dialog.initOwner(window);\n    }\n\n    private void setContentText(final String contentText) {\n        message.setText(contentText);\n    }\n\n    private void setGraphic(final Node node) {\n        message.setGraphic(node);\n    }\n\n    private void setButtons(final ButtonType... buttons) {\n        for (final ButtonType buttonType : buttons) {\n            final Button button = new Button(buttonType.getText());\n            ButtonBar.setButtonData(button, buttonType.getButtonData());\n\n            button.setOnAction(event -> {\n                Alert.this.buttonType = buttonType;\n                ((Stage) parent.get().getWindow()).close();\n            });\n\n            buttonBar.getButtons().add(button);\n        }\n    }\n\n    private Optional<ButtonType> getButtonType() {\n        return Optional.ofNullable(buttonType);\n    }\n\n    public Optional<ButtonType> showAndWait() {\n        dialog.sizeToScene();\n        dialog.setResizable(false);\n        dialog.showAndWait();\n\n        return getButtonType();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/AutoCompleteTextField.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.input.KeyEvent;\n\nimport jgnash.uifx.control.autocomplete.AutoCompleteModel;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Text field for auto completion of values.\n *\n * @author Craig Cavanaugh\n */\npublic class AutoCompleteTextField<E> extends TextFieldEx {\n\n    private final ObjectProperty<AutoCompleteModel<E>> autoCompleteModel = new SimpleObjectProperty<>();\n\n    public AutoCompleteTextField() {\n        // If the enter key is pressed to accept the auto complete,\n        // simulate a tab key press to focus the next field\n        addEventFilter(KeyEvent.KEY_PRESSED, event -> {\n            if (JavaFXUtils.ENTER_KEY.match(event)) {\n                JavaFXUtils.runLater(() -> JavaFXUtils.focusNext(AutoCompleteTextField.this));\n            }\n        });\n    }\n\n    @Override\n    public void replaceText(final int start, final int end, final String text) {\n        super.replaceText(start, end, text);\n\n        if (autoCompleteModel.get() != null) {\n            final String currText = getText(); // get the full string\n            final String newText = autoCompleteModel.get().doLookAhead(currText); // look for a match\n\n            if (newText != null && !currText.isEmpty()) { // found a match and the field is not empty\n                clear(); // clear existing text\n\n                if (start + 1 > currText.length()) {    // delete action has occurred\n                    setText(currText.substring(0, start));\n                    positionCaret(start);\n                } else {\n                    // replace with the new text string\n                    super.replaceText(0, 0, currText.substring(0, start + 1) + newText.substring(start + 1));\n\n                    // highlight the remainder of the auto-completed text\n                    positionCaret(start + 1);\n                    selectEnd();\n                }\n            }\n        }\n    }\n\n    @Override\n    public void deleteText(final int start, final int end) {\n        super.replaceText(start, end, \"\");  // force call to super super.replaceText to prevent bounds errors\n    }\n\n    public ObjectProperty<AutoCompleteModel<E>> autoCompleteModelObjectProperty() {\n        return autoCompleteModel;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/BigDecimalTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.input.KeyCode;\n\n/**\n * A class containing a {@link TableCell} implementation that draws a {@link DecimalTextField} node inside the cell.\n * <p>\n * By default, the BigDecimalTableCell is rendered as a {@link javafx.scene.control.Label} when not being edited, and\n * as a DecimalTextField when in editing mode.\n *\n * @author Craig Cavanaugh\n */\npublic class BigDecimalTableCell<S> extends TableCell<S, BigDecimal> {\n\n    private final SimpleObjectProperty<NumberFormat> numberFormat = new SimpleObjectProperty<>();\n\n    private DecimalTextField decimalTextField = null;\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Boolean> focusChangeListener;\n\n    public BigDecimalTableCell(final ObjectProperty<NumberFormat> numberFormatProperty) {\n        setStyle(\"-fx-alignment: center-right;\");  // Right align\n        numberFormatProperty().bind(numberFormatProperty);\n    }\n\n    public BigDecimalTableCell(final NumberFormat numberFormat) {\n        setStyle(\"-fx-alignment: center-right;\");  // Right align\n        numberFormatProperty().set(numberFormat);\n    }\n\n    @Override\n    protected void updateItem(final BigDecimal amount, final boolean empty) {\n        super.updateItem(amount, empty);  // required\n\n        if (empty) {\n            setText(null);\n            setGraphic(null);\n        } else {\n            if (isEditing()) {\n                setText(null);\n                getDecimalTextField().setDecimal(getItem());\n                setGraphic(getDecimalTextField());\n            } else if (amount != null) {\n                setText(numberFormat.get().format(amount));\n                setGraphic(null);\n            } else {\n                setText(null);\n                setGraphic(null);\n            }\n        }\n    }\n\n    private SimpleObjectProperty<NumberFormat> numberFormatProperty() {\n        return numberFormat;\n    }\n\n    @Override\n    public void startEdit() {\n        if (!isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) {\n            return;\n        }\n\n        final DecimalTextField decimalTextField = getDecimalTextField();\n        decimalTextField.setDecimal(getItem());\n\n        super.startEdit();\n        setText(null);\n        setGraphic(decimalTextField);\n        decimalTextField.requestFocus();\n    }\n\n    @Override\n    public void cancelEdit() {\n        super.cancelEdit();\n\n        setText(numberFormat.get().format(getItem()));\n        setGraphic(null);\n    }\n\n    private DecimalTextField getDecimalTextField() {\n        if (decimalTextField == null) {\n            decimalTextField = new DecimalTextField();\n            decimalTextField.scaleProperty().set(numberFormat.get().getMaximumFractionDigits());\n\n            decimalTextField.setOnKeyPressed(event -> {\n                if (isEditing() && event.getCode() == KeyCode.ENTER) {\n                    commitEdit(decimalTextField.getDecimal());\n                }\n            });\n\n            focusChangeListener = (observable, oldValue, newValue) -> {\n                if (isEditing() && !newValue) {\n                    commitEdit(decimalTextField.getDecimal());\n                }\n            };\n\n            decimalTextField.focusedProperty().addListener(new WeakChangeListener<>(focusChangeListener));\n        }\n\n        return decimalTextField;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/BusyPane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.concurrent.Task;\nimport javafx.concurrent.WorkerStateEvent;\nimport javafx.geometry.Pos;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ProgressIndicator;\nimport javafx.scene.effect.BoxBlur;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.text.Font;\nimport javafx.scene.text.FontWeight;\n\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.util.Nullable;\n\n/**\n * A busy pane.  Intended to overlay the main ui and blur the display when a long running operation is occurring\n *\n * @author Craig Cavanaugh\n */\npublic class BusyPane extends StackPane {\n\n    private ImageView imageView;\n\n    private final Label messageLabel;\n\n    private final ProgressIndicator progressIndicator;\n\n    /**\n     * Listens for changes to the font scale\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<Number> fontScaleListener;\n\n    public BusyPane() {\n\n        progressIndicator = new ProgressIndicator();\n        messageLabel = new Label();\n\n        final GridPane gridPane = new GridPane();\n        gridPane.setAlignment(Pos.CENTER);\n        gridPane.setHgap(15);\n        gridPane.add(progressIndicator, 0, 0);\n        gridPane.add(messageLabel, 1, 0);\n\n        updateFont();\n\n        fontScaleListener = (observable, oldValue, newValue) -> updateFont();\n        ThemeManager.fontScaleProperty().addListener(new WeakChangeListener<>(fontScaleListener));\n\n        getChildren().addAll(gridPane);\n\n        setVisible(false);\n    }\n\n    private ImageView getImageView() {\n        ImageView imageView = new ImageView();\n        imageView.setFocusTraversable(false);\n        imageView.setEffect(new BoxBlur(4, 4, 2));\n\n        imageView.setImage(getScene().getRoot().snapshot(null, null));\n\n        return imageView;\n    }\n\n    private void updateFont() {\n        messageLabel.fontProperty().set(Font.font(null, FontWeight.BOLD, null,\n                ThemeManager.getBaseTextHeight() * 1.2));\n    }\n\n    public void setTask(@Nullable final Task<?> task) {\n        if (task == null) {\n            setVisible(false);\n            progressIndicator.progressProperty().unbind();\n            messageLabel.textProperty().unbind();\n            getChildren().remove(imageView);\n\n            if (imageView != null) {    // protect against a race condition\n                imageView.setImage(null);   // don't retain the image, conserve memory\n                imageView = null;\n            }\n        } else {\n\n            // get the snapshot\n            imageView = getImageView();\n\n            messageLabel.textProperty().bind(task.messageProperty());\n            progressIndicator.progressProperty().bind(task.progressProperty());\n\n            // Add event handlers to automatically hide the busy pane.\n            // These handler will not override the setOnXxx handlers\n            task.addEventHandler(WorkerStateEvent.WORKER_STATE_SUCCEEDED, event -> setTask(null));\n            task.addEventHandler(WorkerStateEvent.WORKER_STATE_CANCELLED, event -> setTask(null));\n            task.addEventHandler(WorkerStateEvent.WORKER_STATE_FAILED, event -> setTask(null));\n\n            setVisible(true);\n        }\n    }\n\n    @Override protected void layoutChildren() {\n        super.layoutChildren();\n\n        if (getParent() != null && isVisible()) {\n            setVisible(false);\n            getChildren().remove(imageView);\n            getChildren().add(imageView);\n            imageView.toBack();\n            setVisible(true);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/CheckComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.collections.ListChangeListener;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.control.cell.CheckBoxListCell;\nimport javafx.scene.input.MouseEvent;\n\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * ComboBox of checked items\n *\n * @author Craig Cavanaugh\n */\npublic class CheckComboBox<T> extends ComboBox<T> {\n\n    private final Map<T, BooleanProperty> itemBooleanPropertyMap = new HashMap<>();\n\n    private final List<ChangeListener<Boolean>> checkChangedListeners = new ArrayList<>();\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ListCell<T> buttonCell;\n\n    private final ChangeListener<Boolean> checkedChangeListener = (observable, oldValue, newValue) -> {\n        for (final ChangeListener<Boolean> listener : checkChangedListeners) {\n            listener.changed(observable, oldValue, newValue);\n        }\n    };\n\n    public CheckComboBox() {\n        setCellFactory(param -> {\n            final ListCell<T> cell = new CheckBoxListCell<>(CheckComboBox.this::getItemBooleanProperty);\n\n            // toggle the value\n            cell.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {\n                itemBooleanPropertyMap.get(cell.getItem()).setValue(!itemBooleanPropertyMap.get(cell.getItem()).get());\n                JavaFXUtils.runLater(CheckComboBox.this::updatePromptText);\n            });\n\n            return cell;\n        });\n\n        // need to override the button cell, otherwise prompt text is replaced by the last selection\n        buttonCell = new ListCell<>() {\n            @Override\n            protected void updateItem(final T item, final boolean empty) {\n                setText(getPromptText());\n            }\n        };\n\n        setButtonCell(buttonCell);\n\n        getItems().addListener((ListChangeListener<T>) c -> JavaFXUtils.runLater(CheckComboBox.this::updatePromptText));\n    }\n\n    /**\n     * Adds a ChangeListener which will be notified whenever a check changes.\n     *\n     * @param listener The listener to register\n     */\n    public void addListener(final ChangeListener<Boolean> listener) {\n        checkChangedListeners.add(listener);\n    }\n\n    public void add(T item, boolean check) {\n        getItems().add(item);\n        getItemBooleanProperty(item).setValue(check);\n    }\n\n    public List<T> getCheckedItems() {\n        final List<T> checkedItems = new ArrayList<>();\n\n        for (final T item : getItems()) {\n            if (getItemBooleanProperty(item).get()) {\n                checkedItems.add(item);\n            }\n        }\n\n        return checkedItems;\n    }\n\n    public void setChecked(T item, boolean check) {\n        getItemBooleanProperty(item).setValue(check);\n    }\n\n    public void setAllChecked() {\n        for (final T item : getItems()) {\n            getItemBooleanProperty(item).set(true);\n        }\n    }\n\n    private void updatePromptText() {\n        final StringBuilder sb = new StringBuilder();\n\n        getItems().filtered(t -> getItemBooleanProperty(t).get())\n                .forEach(t -> sb.append(\", \").append(t.toString()));\n\n        // strip the leading space and comma\n        final String string = sb.substring(Integer.min(2, sb.length()));\n\n        setPromptText(string);\n        setPromptText(string);\n        setTooltip(new Tooltip(string));\n    }\n\n    private BooleanProperty getItemBooleanProperty(final T item) {\n        if (itemBooleanPropertyMap.get(item) == null) {\n            SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty();\n            itemBooleanPropertyMap.put(item, booleanProperty);\n            booleanProperty.addListener(checkedChangeListener);\n        }\n\n        return itemBooleanPropertyMap.get(item);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/CheckListView.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.collections.FXCollections;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.cell.CheckBoxListCell;\n\n/**\n * CheckListView control\n *\n * @author Craig Cavanaugh\n */\npublic class CheckListView<T> extends ListView<T> {\n\n    private final Map<T, BooleanProperty> itemMap = new HashMap<>();\n\n    public CheckListView() {\n        super(FXCollections.observableArrayList());\n\n        setCellFactory(listView -> new CheckBoxListCell<>(this::getItemBooleanProperty));\n    }\n\n    private BooleanProperty getItemBooleanProperty(final T item) {\n\n        itemMap.putIfAbsent(item, new SimpleBooleanProperty());\n\n        return itemMap.get(item);\n    }\n\n    public List<T> getCheckedItems() {\n        final List<T> checkedItems = new ArrayList<>();\n\n        for (final T item : getItems()) {\n            if (getItemBooleanProperty(item).get()) {\n                checkedItems.add(item);\n            }\n        }\n\n        return checkedItems;\n    }\n\n    public void clearChecks() {\n        for (final T item : getItems()) {\n            getItemBooleanProperty(item).set(false);\n        }\n    }\n\n    public void checkAll() {\n        for (final T item : getItems()) {\n            getItemBooleanProperty(item).set(true);\n        }\n    }\n\n    public void toggleAll() {\n        for (final T item : getItems()) {\n            getItemBooleanProperty(item).set(!getItemBooleanProperty(item).get());\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/ChoiceDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.Collection;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.stage.Stage;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * A ChoiceDialog with a consistent application appearance\n *\n * @param <T> The type of the items to show to the user, and the type that is returned\n *            via {@link #showAndWait()} when the dialog is dismissed.\n *\n * @author Craig Cavanaugh\n */\npublic class ChoiceDialog<T> {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private  ButtonBar buttonBar;\n\n    @FXML\n    private  Button okButton;\n\n    @FXML\n    private  Button cancelButton;\n\n    @FXML\n    private Label message;\n\n    @FXML\n    private  ComboBox<T> comboBox;\n\n    private final Stage dialog;\n\n    /**\n     * Creates a new ChoiceDialog instance with the first argument specifying the\n     * default choice that should be shown to the user, and the second argument\n     * specifying a collection of all available choices for the user. It is\n     * expected that the defaultChoice be one of the elements in the choices\n     * collection. If this is not true, then defaultChoice will be set to null and the\n     * dialog will show with the initial choice set to the first item in the list\n     * of choices.\n     *\n     * @param defaultChoice The item to display as the pre-selected choice in the dialog.\n     *        This item must be contained within the choices varargs array.\n     * @param choices All possible choices to present to the user.\n     */\n    public ChoiceDialog(final T defaultChoice, final Collection<T> choices) {\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        dialog = FXMLUtils.loadFXML(this, \"ChoiceDialog.fxml\", resources);\n\n        setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.QUESTION_CIRCLE,\n                ThemeManager.getBaseTextHeight() * Alert.HEIGHT_MULTIPLIER));\n\n        // block until the combo box is completed loaded to prevent a race condition\n        JavaFXUtils.runAndWait(() -> {\n            comboBox.getItems().setAll(choices);\n            setSelectedItem(defaultChoice);\n        });\n    }\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        okButton.setOnAction(event -> handleOkayAction());\n        cancelButton.setOnAction(event -> handleCancelAction());\n        okButton.disableProperty().bind(comboBox.valueProperty().isNull());\n    }\n\n    public void setTitle(final String title) {\n        dialog.setTitle(title);\n    }\n\n    public void setContentText(final String contentText) {\n        message.setText(contentText);\n        dialog.sizeToScene();\n    }\n\n    private void setGraphic(final Node node) {\n        message.setGraphic(node);\n    }\n\n    /**\n     * Returns the currently selected item in the dialog.\n     * @return the currently selected item\n     */\n    private T getSelectedItem() {\n        return comboBox.getSelectionModel().getSelectedItem();\n    }\n\n    /**\n     * Sets the currently selected item in the dialog.\n     * @param item The item to select in the dialog.\n     */\n    private void setSelectedItem(T item) {\n        comboBox.getSelectionModel().select(item);   }\n\n    public Optional<T> showAndWait() {\n        dialog.sizeToScene();\n        dialog.setResizable(false);\n        dialog.showAndWait();\n        return Optional.ofNullable(getSelectedItem());\n    }\n\n    private void handleOkayAction() {\n        ((Stage)parent.get().getWindow()).close();\n    }\n\n    private void handleCancelAction() {\n        comboBox.setValue(null);\n        ((Stage)parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/CurrencyComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.List;\nimport java.util.Objects;\n\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * ComboBox that allows selection of a CurrencyNode and manages it's own model.\n *\n * @author Craig Cavanaugh\n */\npublic class CurrencyComboBox extends ComboBox<CurrencyNode> implements MessageListener{\n\n    /** Model for the ComboBox. */\n    private ObservableList<CurrencyNode> items;\n\n    public CurrencyComboBox() {\n        JavaFXUtils.runLater(this::loadModel); // lazy load to let the ui build happen faster\n    }\n\n    private void loadModel() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<CurrencyNode> nodeList = engine.getCurrencies();\n\n        // extract and reuse the default model\n        items = getItems();\n\n        // warp in a sorted list\n        setItems(new SortedList<>(items, null));\n\n        items.addAll(nodeList);\n\n        final CurrencyNode defaultCurrency = engine.getDefaultCurrency();\n        setValue(defaultCurrency);\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.COMMODITY, MessageChannel.SYSTEM);\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        if (event.getObject(MessageProperty.COMMODITY) instanceof CurrencyNode) {\n\n            final CurrencyNode node = event.getObject(MessageProperty.COMMODITY);\n\n            JavaFXUtils.runLater(() -> {\n                switch (event.getEvent()) {\n                    case CURRENCY_REMOVE:\n                        items.removeAll(node);\n                        break;\n                    case CURRENCY_ADD:\n                        items.add(node);\n                        break;\n                    case CURRENCY_MODIFY:\n                        items.removeAll(node);\n                        items.add(node);\n                        break;\n                    case FILE_CLOSING:\n                        items.clear();\n                    default:\n                        break;\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/DataStoreTypeComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * ComboBox that allows selection of a CurrencyNode and manages it's own model.\n *\n * @author Craig Cavanaugh\n */\npublic class DataStoreTypeComboBox extends ComboBox<DataStoreType> {\n\n    public DataStoreTypeComboBox() {\n        setEditable(false);\n\n        JavaFXUtils.runLater(this::loadModel); // lazy load to let the ui build happen faster\n    }\n\n    private void loadModel() {\n        // extract and reuse the default model\n        ObservableList<DataStoreType> items = getItems();\n\n        // warp in a sorted list\n        setItems(new SortedList<>(items, null));\n\n        items.addAll(DataStoreType.values());\n\n        setValue(DataStoreType.H2_DATABASE);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/DatePickerEx.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.input.KeyEvent;\nimport javafx.util.converter.LocalDateStringConverter;\n\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Enhanced DatePicker.  Adds short cuts for date entry with better input character filters\n *\n * @author Craig Cavanuugh\n */\npublic class DatePickerEx extends DatePicker {\n\n    private final String allowedDateCharacters;\n\n    private final DateTimeFormatter dateFormatter;\n\n    private char dateFormatSeparator = '/';\n\n    /**\n     * Strong Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<Boolean> focusChangeListener;\n\n    /**\n     * Strong Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<LocalDate> valueListener;\n\n    private final BooleanProperty preserveDate = new SimpleBooleanProperty(false);\n\n    private final StringProperty preferenceKey = new SimpleStringProperty();\n\n    private final ObjectProperty<Preferences> preferences = new SimpleObjectProperty<>();\n\n    /**\n     * Composite to make handling of bindings simpler\n     */\n    private final BooleanProperty saveRestoreDate = new SimpleBooleanProperty(false);\n\n    /**\n     * One shot restoration of the data value if enabled\n     */\n    private final AtomicBoolean oldValueRestored = new AtomicBoolean(false);\n\n    public DatePickerEx() {\n        super(LocalDate.now()); // initialize with a valid date\n\n        saveRestoreDate.bind(preferenceKey.isNotEmpty().and(preferences.isNotNull()).and(preserveDate));\n\n        // restore the date when the property goes from false to true\n        saveRestoreDate.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null && newValue && !oldValueRestored.get()) {\n                oldValueRestored.set(true);\n\n                Preferences preferences = preferencesProperty().get();\n                final long value = preferences.getLong(preferenceKeyProperty().get(), -1);\n\n                if (value > 0) {\n                    final LocalDate restoreDate = DateUtils.asLocalDate(value);\n                    JavaFXUtils.runLater(() -> setValue(restoreDate));\n                }\n            }\n        });\n\n        final StringBuilder buf = new StringBuilder(\"0123456789\");\n\n        dateFormatter = DateUtils.getShortDateManualEntryFormatter();\n\n        final char[] chars = dateFormatter.format(LocalDate.now()).toCharArray();\n\n        for (final char aChar : chars) {\n            if (!Character.isDigit(aChar)) {\n                if (buf.indexOf(Character.toString(aChar)) == -1) {\n                    buf.append(aChar);\n\n                    dateFormatSeparator = aChar;\n                }\n            }\n        }\n\n        allowedDateCharacters = buf.toString();\n\n        setConverter(new LocalDateStringConverter(dateFormatter, dateFormatter));\n\n        // Handle horizontal and vertical scroll wheel events\n        getEditor().setOnScroll(event -> {\n            final int caretPosition = getEditor().getCaretPosition();\n            final LocalDate date = _getValue();\n\n            if (event.getDeltaY() > 0) {\n                JavaFXUtils.runLater(() -> {\n                    setValue(date.plusDays(1));\n                    getEditor().positionCaret(caretPosition);\n                });\n            } else if (event.getDeltaY() < 0) {\n                JavaFXUtils.runLater(() -> {\n                    setValue(date.minusDays(1));\n                    getEditor().positionCaret(caretPosition);\n                });\n            }\n\n            if (event.getDeltaX() > 0) {\n                JavaFXUtils.runLater(() -> {\n                    setValue(date.plusMonths(1));\n                    getEditor().positionCaret(caretPosition);\n                });\n            } else if (event.getDeltaX() < 0) {\n                JavaFXUtils.runLater(() -> {\n                    setValue(date.minusMonths(1));\n                    getEditor().positionCaret(caretPosition);\n                });\n            }\n        });\n\n        getEditor().addEventFilter(KeyEvent.KEY_TYPED, event -> {\n\n            // An empty event character is possible... must protect against it\n            if (!event.getCharacter().isEmpty() && allowedDateCharacters.indexOf(event.getCharacter().charAt(0)) < 0) {\n                event.consume();\n            }\n        });\n\n        focusChangeListener = (observable, oldValue, newValue) -> JavaFXUtils.runLater(() -> {\n            final int caretPosition = getEditor().getCaretPosition();\n            getEditor().setText(dateFormatter.format(_getValue()));\n            getEditor().positionCaret(caretPosition);\n        });\n\n        // Ensure the last parsable value is displayed after focus is lost.\n        // Wrap in a weak change listener to protect against memory leaks.\n        getEditor().focusedProperty().addListener(new WeakChangeListener<>(focusChangeListener));\n\n        getEditor().addEventHandler(KeyEvent.KEY_PRESSED, event -> {\n            final int caretPosition = getEditor().getCaretPosition();   // preserve caret position for restoration\n            final LocalDate date = _getValue(); // force an update to the current value\n\n            switch (event.getCode()) {\n                case PERIOD:    // substitute common separators with the current locale's SEPARATOR\n                case SLASH:     // while preventing entry of consecutive separators\n                case COMMA:\n                case BACK_SLASH:\n                    JavaFXUtils.runLater(() -> {\n                        final StringBuilder text = new StringBuilder(getEditor().getText());\n\n                        if (text.length() > caretPosition) {\n                            if (text.charAt(caretPosition) != dateFormatSeparator && (caretPosition > 0\n                                                                                              && text.charAt(caretPosition - 1) != dateFormatSeparator)) {\n                                text.insert(caretPosition, dateFormatSeparator);\n                            }\n                        } else {\n                            text.append(dateFormatSeparator);\n                        }\n                        getEditor().setText(text.toString());\n                        getEditor().positionCaret(caretPosition + 1);\n                    });\n                    break;\n                case ADD:\n                case UP:\n                case KP_UP:\n                    JavaFXUtils.runLater(() -> {\n                        setValue(date.plusDays(1));\n                        getEditor().positionCaret(caretPosition);\n                    });\n                    break;\n                case SUBTRACT:\n                case DOWN:\n                case KP_DOWN:\n                    JavaFXUtils.runLater(() -> {\n                        setValue(date.minusDays(1));\n                        getEditor().positionCaret(caretPosition);\n                    });\n                    break;\n                case T:\n                    JavaFXUtils.runLater(() -> {\n                        setValue(LocalDate.now());\n                        getEditor().positionCaret(caretPosition);\n                    });\n                    break;\n                case PAGE_UP:\n                    JavaFXUtils.runLater(() -> {\n                        setValue(date.plusMonths(1));\n                        getEditor().positionCaret(caretPosition);\n                    });\n                    break;\n                case PAGE_DOWN:\n                    JavaFXUtils.runLater(() -> {\n                        setValue(date.minusMonths(1));\n                        getEditor().positionCaret(caretPosition);\n                    });\n                    break;\n                default:\n            }\n        });\n\n        valueListener = (observable, oldValue, newValue) -> {\n            if (newValue == null) {\n                setValue(oldValue);\n            } else if (saveRestoreDate.get()) { // save the date\n                preferencesProperty().get().putLong(preferenceKeyProperty().get(), DateUtils.asEpochMilli(newValue));\n            }\n        };\n\n        // Wrap in a weak change listener to protect against memory leaks.\n        valueProperty().addListener(new WeakChangeListener<>(valueListener));\n    }\n\n    private LocalDate _getValue() {\n        try {\n            final LocalDate date = LocalDate.parse(getEditor().getText(), dateFormatter);\n            setValue(date);\n            return date;\n        } catch (final DateTimeParseException ignored) {\n            return getValue();  // return the current value\n        }\n    }\n\n    public BooleanProperty preserveDateProperty() {\n        return preserveDate;\n    }\n\n    public StringProperty preferenceKeyProperty() {\n        return preferenceKey;\n    }\n\n    public ObjectProperty<Preferences> preferencesProperty() {\n        return preferences;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/DateRangeDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.time.LocalDate;\nimport java.util.Optional;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ButtonBar;\nimport javafx.stage.Stage;\n\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Simple date range input controller.\n *\n * @author Craig Cavanaugh\n */\npublic class DateRangeDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    private LocalDate[] dates = null;\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        startDatePicker.setValue(endDatePicker.getValue().minusYears(1));\n    }\n\n    public Optional<LocalDate[]> getDates() {\n        return Optional.ofNullable(dates);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleOkAction() {\n        dates = new LocalDate[] {startDatePicker.getValue(), endDatePicker.getValue()};\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/DecimalTextField.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.math.BigDecimal;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.Objects;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyBooleanProperty;\nimport javafx.beans.property.ReadOnlyDoubleProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.MathEval;\nimport jgnash.util.NotNull;\n\n/**\n * Text field for entering decimal values.\n *\n * @author Craig Cavanaugh\n */\npublic class DecimalTextField extends TextFieldEx {\n\n    private static final int DEFAULT_SCALE = 2;\n\n    /**\n     * Allowable character in input.\n     */\n    private static final String FLOAT;\n\n    /**\n     * Allowable math operators in input.\n     */\n    private static final String MATH_OPERATORS = \"()+*/\";\n\n    private static char group = ',';\n\n    private static char fraction = '.';\n\n    /**\n     * Used for output of parsed input.\n     */\n    private final NumberFormat format;\n\n    /**\n     * Used to track state of fractional SEPARATOR input on numeric pad.\n     */\n    private volatile boolean forceFraction = false;\n\n    // the property value may be null\n    private final ObjectProperty<BigDecimal> decimal = new SimpleObjectProperty<>();\n\n    private final SimpleDoubleProperty doubleValue = new SimpleDoubleProperty();\n\n    /**\n     * Controls the maximum number of displayed decimal places.\n     */\n    private final SimpleIntegerProperty scale = new SimpleIntegerProperty();\n\n    /**\n     * Controls the minimum number of displayed decimal places.\n     */\n    private final SimpleIntegerProperty minScale = new SimpleIntegerProperty();\n\n    /**\n     * Displays an empty field if {@code decimalProperty} is zero.\n     */\n    private final BooleanProperty emptyWhenZero = new SimpleBooleanProperty(true);\n\n    private final BooleanProperty isValid = new SimpleBooleanProperty(false);\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<Boolean> focusChangeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<String> validValueListener;\n\n    static {\n        FLOAT = getAllowedChars();\n    }\n\n    public DecimalTextField() {\n        format = NumberFormat.getInstance();\n\n        /* Disable grouping for output formatting on all locales.\n         * This solves issues with parsing out group separators.\n         * This does not prevent parsing grouping input.\n         */\n        format.setGroupingUsed(false);\n\n        // Force evaluation on loss of focus\n        focusChangeListener = (observable, oldValue, newValue) -> {\n            if (!newValue) {\n                evaluateAndSet();\n            }\n        };\n\n        // Listen to any text changes to determine if it\n        validValueListener = (observable, oldValue, newValue) -> {\n            try {\n                // Replace any commas with decimals before trying to parse the string\n                Double.parseDouble(newValue.replace(',', '.'));\n                isValid.setValue(true);\n            } catch (Exception e) {\n                isValid.setValue(false);\n            }\n        };\n\n        focusedProperty().addListener(new WeakChangeListener<>(focusChangeListener));\n\n        textProperty().addListener(new WeakChangeListener<>(validValueListener));\n\n        addEventFilter(KeyEvent.KEY_PRESSED, event -> {\n            //Raise a flag the Decimal key has been pressed so it can be\n            // forced to a comma or period based on locale\n            if (event.getCode() == KeyCode.DECIMAL) {\n                forceFraction = true;\n            }\n\n            // For evaluation if the enter key is pressed\n            if (event.getCode() == KeyCode.ENTER) {\n                JavaFXUtils.runLater(() -> {\n                    evaluateAndSet();\n                    positionCaret(getText().length());\n                });\n            }\n        });\n\n        decimalProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue == null || (emptyWhenZeroProperty().get() && newValue.compareTo(BigDecimal.ZERO) == 0)) {\n                setText(\"\");\n            } else {\n                setText(format.format(newValue.doubleValue()));\n            }\n        });\n\n        // Change the max and minimum scale allowed for entry\n        scale.addListener((observable, oldValue, newValue) -> {\n            if (format instanceof DecimalFormat) {\n                format.setMaximumFractionDigits(scale.get());\n            }\n            evaluateAndSet();\n        });\n\n        minScale.addListener((observable, oldValue, newValue) -> {\n            if (format instanceof DecimalFormat) {\n                format.setMinimumFractionDigits(minScale.get());\n            }\n            evaluateAndSet();\n        });\n\n        scale.set(DEFAULT_SCALE); // trigger update to the format\n        minScale.set(DEFAULT_SCALE);\n    }\n\n    public ObjectProperty<BigDecimal> decimalProperty() {\n        return decimal;\n    }\n\n    /**\n     * {@code ReadOnlyDoubleProperty} representation of the {@code BigDecimal} property to make bindings and error checking easier\n     *\n     * @return ReadOnlyDoubleProperty\n     */\n    public ReadOnlyDoubleProperty doubleProperty() {\n        return ReadOnlyDoubleProperty.readOnlyDoubleProperty(doubleValue);\n    }\n\n    /**\n     * Property indicating if the field contains a valid numeric value\n     *\n     * @return ReadOnlyBooleanProperty\n     */\n    public ReadOnlyBooleanProperty validDecimalProperty() {\n        return ReadOnlyBooleanProperty.readOnlyBooleanProperty(isValid);\n    }\n\n    public IntegerProperty scaleProperty() {\n        return scale;\n    }\n\n    public IntegerProperty minScaleProperty() {\n        return minScale;\n    }\n\n    private void evaluateAndSet() {\n        final String t = evaluateInput();\n        if (!t.isEmpty()) {\n            // round the value to scale\n            setDecimal(new BigDecimal(t).setScale(scale.get(), MathConstants.roundingMode));\n        } else {\n            setDecimal(BigDecimal.ZERO);\n        }\n    }\n\n    /**\n     * Sets the decimal value for the field.\n     *\n     * @param decimal {@code BigDecimal}\n     */\n    public void setDecimal(@NotNull final BigDecimal decimal) {\n        Objects.requireNonNull(decimal);\n\n        this.decimal.set(decimal.setScale(scale.get(), MathConstants.roundingMode));\n        doubleValue.setValue(this.decimal.getValue().doubleValue());    // set the double property\n    }\n\n    public @NotNull\n    BigDecimal getDecimal() {\n        if (!isEmpty()) {\n            try {\n                return new BigDecimal(evaluateInput());\n            } catch (final NumberFormatException ignored) {\n                // ignore and drop out\n            }\n        }\n        return BigDecimal.ZERO;\n    }\n\n    /**\n     * Determines if the field is empty.\n     *\n     * @return {@code false} if empty\n     */\n    private boolean isEmpty() {\n        boolean result = true;\n\n        if (getText() != null) {\n            result = getText().isEmpty();\n        }\n\n        return result;\n    }\n\n    @Override\n    public void deleteText(int start, int end) {\n        super.replaceText(start, end, \"\");\n    }\n\n    @Override\n    public void replaceText(final int start, final int end, final String text) {\n        Objects.requireNonNull(text);\n\n        final String newText = getText().substring(0, start) + text + getText().substring(end);\n\n        /* fraction input is handled as a special case */\n        if (forceFraction) {\n            super.replaceText(start, end, Character.toString(fraction));\n            forceFraction = false;\n            return;\n        }\n\n        for (int i = 0; i < newText.length(); i++) {\n            if (!FLOAT.contains(String.valueOf(newText.charAt(i)))) {\n                return;\n            }\n        }\n        super.replaceText(start, end, text);\n    }\n\n    @Override\n    public void replaceSelection(final String text) {\n        final int start = getSelection().getStart();\n        final int end = getSelection().getEnd();\n        final String newText = getText().substring(0, start) + text + getText().substring(end);\n\n        for (int j = 0; j < newText.length(); j++) {\n            if (!FLOAT.contains(String.valueOf(newText.charAt(j)))) {\n                return;\n            }\n        }\n        super.replaceSelection(text);\n\n        positionCaret(end);\n    }\n\n    /**\n     * By default, and numeric values, basic math operators, and '.' and ',' are\n     * allowed.\n     *\n     * @return A string with the characters that are allowed in math expressions\n     */\n    private static String getAllowedChars() {\n        // get grouping and fractional separators\n        final NumberFormat format = NumberFormat.getInstance();\n\n        if (format instanceof DecimalFormat) {\n            group = ((DecimalFormat) format).getDecimalFormatSymbols().getGroupingSeparator();\n            fraction = ((DecimalFormat) format).getDecimalFormatSymbols().getDecimalSeparator();\n        }\n\n        // doctor up some locales so numeric pad works\n        if (group != '.' && fraction == ',') { // grouping symbol is odd\n            group = '.';\n        }\n\n        return \"-0123456789\" + group + fraction + MATH_OPERATORS;\n    }\n\n    /**\n     * BigDecimal and the interpreter cannot parse ',' in string\n     * representations of decimals. This method will replace any ',' with '.'\n     * and then try parsing with BigDecimal. If this fails, then it is assumed\n     * that the user has used mathematical operators and then evaluates the\n     * string as a mathematical expression.\n     *\n     * @return A string representation of the resulting decimal\n     */\n    @NotNull\n    private String evaluateInput() {\n        String text = getText();\n\n        if (text == null || text.isEmpty()) {\n            return \"\";\n        }\n\n        // strip out any group separators (This could be '.' for certain locales)\n        final StringBuilder temp = new StringBuilder();\n\n        for (int i = 0; i < text.length(); i++) {\n            char c = text.charAt(i);\n            if (c != group) {\n                temp.append(c);\n            }\n        }\n\n        text = temp.toString();\n\n        // replace any ',' with periods so that it can be parsed correctly\n        if (fraction == ',') {\n            text = text.replace(',', '.');\n        }\n\n        try {\n            doubleValue.setValue(new BigDecimal(text).doubleValue());   // try to set the double property\n            return new BigDecimal(text).toString();\n        } catch (final NumberFormatException nfe) {\n            try {\n                final double val = MathEval.eval(text);\n\n                if (!Double.isNaN(val)) {\n                    final BigDecimal value = new BigDecimal(val);\n\n                    setDecimal(value);\n                    return value.toString();\n                }\n                doubleValue.set(Double.NaN);\n                return \"\";\n            } catch (final ArithmeticException ex) {\n                return \"\";\n            }\n        }\n    }\n\n    public BooleanProperty emptyWhenZeroProperty() {\n        return emptyWhenZero;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/DetailedDecimalTextField.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.math.BigDecimal;\nimport java.util.Objects;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.css.PseudoClass;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.Region;\n\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.util.NotNull;\n\n/**\n * A {@code DecimalTextField} composite that supports use of a popup or dialog by overriding {@code show()}.\n *\n * @author Craig Cavanaugh\n */\npublic class DetailedDecimalTextField extends GridPane {\n\n    /**\n     * The editor for the ComboBox. It is used for both editable text field and non-editable text field.\n     */\n    private ReadOnlyObjectWrapper<DecimalTextField> editor;\n\n    public ObjectProperty<BigDecimal> valueProperty() {\n        return value;\n    }\n\n    private final ObjectProperty<BigDecimal> value = new SimpleObjectProperty<>(this, \"value\");\n\n    /**\n     * Specifies whether the numeric field allows for user input.\n     *\n     * @return the editable property\n     */\n    protected final BooleanProperty editableProperty() {\n        return editable;\n    }\n\n    private final BooleanProperty editable = new SimpleBooleanProperty(this, \"editable\", true) {\n        @Override\n        protected void invalidated() {\n            pseudoClassStateChanged(PSEUDO_CLASS_EDITABLE, get());\n        }\n    };\n\n\n    public DetailedDecimalTextField() {\n\n        // apply the choice box and  date picker styles to the pane\n        getStyleClass().setAll(\"choice-box\", \"date-picker\");\n\n        final Button arrowButton = new Button(\"\", new MaterialDesignLabel(MaterialDesignLabel.MDIcon.PENCIL));\n        arrowButton.getStyleClass().setAll(\"button\", \"arrow-button\");\n        arrowButton.setFocusTraversable(false);\n        arrowButton.setOnAction(event -> show());\n\n        final ColumnConstraints col = new ColumnConstraints();\n        col.setHgrow(Priority.ALWAYS);\n\n        getColumnConstraints().addAll(col);\n\n        // add the controls\n        add(getEditor(), 0, 0);\n        add(arrowButton, 1, 0);\n\n        // we want the GridPane to wrap right around the controls\n        setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);\n\n        // use the focused state of the text field and apply to the pane\n        getEditor().focusedProperty().addListener((observable, oldValue, newValue)\n                -> pseudoClassStateChanged(FOCUSED_PSEUDO_CLASS, newValue));\n\n    }\n\n    private DecimalTextField getEditor() {\n        return editorProperty().get();\n    }\n\n    private ReadOnlyObjectProperty<DecimalTextField> editorProperty() {\n        if (editor == null) {\n            editor = new ReadOnlyObjectWrapper<>(this, \"editor\"); //NON-NLS\n            DecimalTextField field = new DecimalTextField();\n            field.decimalProperty().bindBidirectional(valueProperty());\n            field.editableProperty().bindBidirectional(editableProperty());\n            editor.set(field);\n        }\n        return editor.getReadOnlyProperty();\n    }\n\n    /**\n     * Decimal property.\n     *\n     * @return BigDecimal object property\n     * @see DecimalTextField#decimalProperty()\n     */\n    public ObjectProperty<BigDecimal> decimalProperty() {\n        return getEditor().decimalProperty();\n    }\n\n    /**\n     * Gets the decimal value.\n     *\n     * @return BigDecimal value\n     * @see DecimalTextField#getDecimal()\n     */\n    public @NotNull\n    BigDecimal getDecimal() {\n        return getEditor().getDecimal();\n    }\n\n    /**\n     * Sets the value for the field.\n     *\n     * @param decimal BigDecimal value to display.  May not be null\n     * @see DecimalTextField#setDecimal(BigDecimal)\n     */\n    protected void setDecimal(@NotNull final BigDecimal decimal) {\n        Objects.requireNonNull(decimal);\n\n        getEditor().setDecimal(decimal);\n    }\n\n    /**\n     * Called when the button is clicked.\n     */\n    public void show() {\n        // does nothing by default\n    }\n\n    private static final PseudoClass FOCUSED_PSEUDO_CLASS = PseudoClass.getPseudoClass(\"focused\");\n\n    private static final PseudoClass PSEUDO_CLASS_EDITABLE = PseudoClass.getPseudoClass(\"editable\");\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/DoughnutChart.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.Node;\nimport javafx.scene.chart.PieChart;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.shape.Circle;\nimport javafx.scene.text.Text;\nimport javafx.scene.text.TextAlignment;\n\n/**\n * Doughnut Chart implementation.\n *\n * @author Craig Cavanaugh\n */\npublic class DoughnutChart extends PieChart {\n\n    private static final int RING_WIDTH = 3;\n\n    private final Circle hole;\n\n    private final Text titleText;\n\n    private final Text subTitleText;\n\n    private final StringProperty centerTitle = new SimpleStringProperty();\n\n    private final StringProperty centerSubTitle = new SimpleStringProperty();\n\n    @SuppressWarnings(\"unused\")\n    public DoughnutChart() {\n        this(FXCollections.observableArrayList());\n    }\n\n    private DoughnutChart(final ObservableList<Data> data) {\n        super(data);\n\n        hole = new Circle();\n        hole.setStyle(\"-fx-fill: -fx-background\");\n\n        titleText = new Text();\n        titleText.setStyle(\"-fx-font-size: 1.4em\");\n        titleText.setTextAlignment(TextAlignment.JUSTIFY);\n        titleText.textProperty().bind(centerTitle);\n\n        subTitleText = new Text();\n        subTitleText.setStyle(\"-fx-font-size: 1.0em\");\n        subTitleText.setTextAlignment(TextAlignment.JUSTIFY);\n        subTitleText.textProperty().bind(centerSubTitle);\n    }\n\n    public StringProperty centerTitleProperty() {\n        return centerTitle;\n    }\n\n    public StringProperty centerSubTitleProperty() {\n        return centerSubTitle;\n    }\n\n    @Override\n    protected void layoutChartChildren(final double top, final double left, final double contentWidth,\n                                       final double contentHeight) {\n\n        super.layoutChartChildren(top, left, contentWidth, contentHeight);\n\n        installContent();\n        updateLayout();\n    }\n\n    private void installContent() {\n        if (!getData().isEmpty()) {\n            final Node node = getData().get(0).getNode();\n            if (node.getParent() instanceof Pane) {\n                final Pane parent = (Pane) node.getParent();\n\n                // The content needs to be reordered after data has changed\n                if (parent.getChildren().contains(hole)) {\n                    parent.getChildren().remove(hole);\n                    parent.getChildren().remove(titleText);\n                    parent.getChildren().remove(subTitleText);\n                }\n\n                if (!parent.getChildren().contains(hole)) {\n                    parent.getChildren().add(hole);\n                    parent.getChildren().add(titleText);\n                    parent.getChildren().add(subTitleText);\n                }\n            }\n        }\n    }\n\n    private void updateLayout() {\n\n        // Determine maximums and minimums, make use of available processors\n        final double minX = getData().parallelStream().mapToDouble(value -> value.getNode().getBoundsInParent()\n                .getMinX()).min().orElse(0);\n\n        final double minY = getData().parallelStream().mapToDouble(value -> value.getNode().getBoundsInParent()\n                .getMinY()).min().orElse(0);\n\n        final double maxX = getData().parallelStream().mapToDouble(value -> value.getNode().getBoundsInParent()\n                .getMaxX()).max().orElse(Double.MAX_VALUE);\n\n        final double maxY = getData().parallelStream().mapToDouble(value -> value.getNode().getBoundsInParent()\n                .getMaxY()).max().orElse(Double.MAX_VALUE);\n\n        // center the hole and set radius\n        hole.setCenterX(minX + (maxX - minX) / 2);\n        hole.setCenterY(minY + (maxY - minY) / 2);\n        hole.setRadius((maxX - minX) / RING_WIDTH);\n\n        // center the title and subtitle\n        titleText.setX((minX + (maxX - minX) / 2) - titleText.getLayoutBounds().getWidth() / 2);\n        titleText.setY(minY + (maxY - minY) / 2);\n\n        subTitleText.setX((minX + (maxX - minX) / 2) - subTitleText.getLayoutBounds().getWidth() / 2);\n        subTitleText.setY((minY + (maxY - minY) / 2) + subTitleText.getLayoutBounds().getHeight());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/ExceptionDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.input.Clipboard;\nimport javafx.scene.input.DataFormat;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Exception dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class ExceptionDialog {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button clipboardButton;\n\n    @FXML\n    private TextArea textArea;\n\n    @FXML\n    private Button closeButton;\n\n    @FXML\n    private Label message;\n\n    private final Stage dialog;\n\n    private String stackTrace;\n\n    private final Throwable throwable;\n\n    public ExceptionDialog(final Throwable throwable) {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        this.throwable = throwable;\n\n        dialog = FXMLUtils.loadFXML(this, \"ExceptionDialog.fxml\", resources);\n        dialog.setTitle(resources.getString(\"Title.UncaughtException\"));\n    }\n\n    @FXML\n    private void initialize() {\n        message.setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.EXCLAMATION_TRIANGLE,\n                ThemeManager.getBaseTextHeight() * Alert.HEIGHT_MULTIPLIER, Color.DARKRED));\n\n        closeButton.setOnAction(event -> ((Stage) parent.get().getWindow()).close());\n\n        clipboardButton.setOnAction(event -> {\n            Clipboard clipboard = Clipboard.getSystemClipboard();\n            Map<DataFormat, Object> map = new HashMap<>();\n            map.put(DataFormat.PLAIN_TEXT, stackTrace);\n            clipboard.setContent(map);\n        });\n    }\n\n    public void showAndWait() {\n        message.setText(throwable.getLocalizedMessage());\n\n        final StringWriter sw = new StringWriter();\n        throwable.printStackTrace(new PrintWriter(sw));\n\n        stackTrace = sw.toString();\n        textArea.setText(stackTrace);\n\n        dialog.setResizable(false);\n        dialog.showAndWait();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/ImageDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.net.MalformedURLException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ToolBar;\nimport javafx.scene.image.Image;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * A dialog for displaying and printing an image.\n *\n * @author Craig Cavanaugh\n */\npublic class ImageDialog {\n\n    private static final int MARGIN = 20;\n    private static final int MIN_SIZE = 100;\n\n    private final Stage dialog;\n    private final ImageView imageView = new ImageView();\n    private final StatusBar statusBar = new StatusBar();\n\n    public static void showImage(final Path path) {\n        final ImageDialog imageDialog = new ImageDialog();\n        imageDialog.setImage(path);\n\n        imageDialog.dialog.show();\n    }\n\n    private ImageDialog() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        dialog = new Stage(StageStyle.DECORATED);\n        dialog.initModality(Modality.APPLICATION_MODAL);\n        dialog.initOwner(MainView.getPrimaryStage());\n        dialog.setTitle(resources.getString(\"Title.ViewImage\"));\n\n        // Set a sane default size\n        dialog.setWidth(450);\n        dialog.setHeight(350);\n\n        final BorderPane borderPane = new BorderPane();\n        final ToolBar toolBar = new ToolBar();\n\n        final Button printButton = new Button(resources.getString(\"Button.Print\"));\n\n        printButton.setOnAction(event -> JavaFXUtils.printImageView(imageView));\n\n        toolBar.getItems().add(printButton);\n\n        final StackPane stackPane = new StackPane();\n        stackPane.getChildren().add(imageView);\n\n        stackPane.setMinSize(MIN_SIZE, MIN_SIZE);\n\n        imageView.fitWidthProperty().bind(stackPane.widthProperty().subtract(MARGIN));\n        imageView.fitHeightProperty().bind(stackPane.heightProperty().subtract(MARGIN));\n\n        borderPane.setTop(toolBar);\n        borderPane.setCenter(stackPane);\n        borderPane.setBottom(statusBar);\n        borderPane.styleProperty().bind(ThemeManager.styleProperty());\n\n        dialog.setScene(new Scene(borderPane));\n        ThemeManager.applyStyleSheets(dialog.getScene());\n\n        // Remember dialog size and location\n        StageUtils.addBoundsListener(dialog, ImageDialog.class, MainView.getPrimaryStage());\n    }\n\n    private void setImage(final Path path) {\n        if (Files.exists(path)) {\n            try {\n                final Image image = new Image(path.toUri().toURL().toString(), true);\n                imageView.setPreserveRatio(true);\n                imageView.setImage(image);\n                statusBar.textProperty().set(path.toString());\n            } catch (final MalformedURLException e) {\n                Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);\n            }\n        } else {\n            StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.MissingAttachment\", path.toString()));\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/IntegerTextField.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.regex.Pattern;\n\n/**\n * Text field for entering integer values.\n *\n * @author Craig Cavanaugh\n */\npublic class IntegerTextField extends TextFieldEx {\n\n    private static final String DIGITS_ONLY_REGEX = \"\\\\b\\\\d+\\\\b\";\n\n    private final Pattern digitOnlyPattern = Pattern.compile(DIGITS_ONLY_REGEX);\n\n    public IntegerTextField() {\n    }\n\n    /**\n     * Sets the {@code Integer} value of the field.\n     *\n     * @param value {@code Integer} value, if null, the field will be cleared\n     */\n    public void setInteger(final Integer value) {\n        if (value != null) {\n            setText(value.toString());\n        } else {\n            setText(\"\");\n        }\n    }\n\n    /**\n     * Sets the {@code Long} value of the field.\n     *\n     * @param value {@code Long} value, if null, the field will be cleared\n     */\n    public void setLong(final Long value) {\n        if (value != null) {\n            setText(value.toString());\n        } else {\n            setText(\"\");\n        }\n    }\n\n    /**\n     * Returns the {@code Integer} value of the field.\n     *\n     * @return the {@code Integer}, zero if the field is empty\n     */\n    public Integer getInteger() {\n        if (getText() != null && getText().length() > 0) {\n            return Integer.parseInt(getText());\n        }\n\n        return 0;\n    }\n\n    /**\n     * Returns the {@code Long} value of the field.\n     *\n     * @return the {@code Long}, zero if the field is empty\n     */\n    public Long getLong() {\n        if (getText() != null && getText().length() > 0) {\n            return Long.parseLong(getText());\n        }\n\n        return 0L;\n    }\n\n    @Override\n    public void deleteText(final int start, final int end) {\n        super.replaceText(start, end, \"\");\n    }\n\n    @Override\n    public void replaceText(final int start, final int end, final String text) {\n        // If the replaced text would end up being invalid, then simply\n        // ignore this call!\n\n        if (digitOnlyPattern.matcher(text).matches()) {\n            super.replaceText(start, end, text);\n        }\n    }\n\n    @Override\n    public void replaceSelection(final String text) {\n        if (digitOnlyPattern.matcher(text).matches()) {\n            super.replaceSelection(text);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/IntegerTreeTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.scene.control.TreeTableCell;\nimport javafx.scene.input.KeyCode;\n\n/**\n * A class containing a {@link TreeTableCell} implementation that draws a {@link IntegerTextField} node inside the cell.\n * <p>\n * By default, the IntegerTreeTableCell is rendered as a {@link javafx.scene.control.Label} when not being edited, and\n * as a IntegerTextField when in editing mode.\n *\n * @author Craig Cavanaugh\n */\npublic class IntegerTreeTableCell<S> extends TreeTableCell<S, Integer> {\n\n    private IntegerTextField integerTextField = null;\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Boolean> focusChangeListener;\n\n    @Override\n    protected void updateItem(final Integer amount, final boolean empty) {\n        super.updateItem(amount, empty);  // required\n\n        if (empty) {\n            setText(null);\n            setGraphic(null);\n        } else {\n            if (isEditing()) {\n                setText(null);\n                getIntegerTextField().setInteger(getItem());\n                setGraphic(getIntegerTextField());\n            } else if (amount != null) {\n                setText(amount.toString());\n                setGraphic(null);\n            } else {\n                setText(null);\n                setGraphic(null);\n            }\n        }\n    }\n\n    @Override\n    public void startEdit() {\n        if (!isEditable() || !getTreeTableView().isEditable() || !getTableColumn().isEditable()) {\n            return;\n        }\n\n        final IntegerTextField decimalTextField = getIntegerTextField();\n        decimalTextField.setInteger(getItem());\n\n        super.startEdit();\n        setText(null);\n        setGraphic(decimalTextField);\n        decimalTextField.requestFocus();\n    }\n\n    @Override\n    public void cancelEdit() {\n        super.cancelEdit();\n\n        setText(getItem().toString());\n        setGraphic(null);\n    }\n\n    private IntegerTextField getIntegerTextField() {\n        if (integerTextField == null) {\n            integerTextField = new IntegerTextField();\n\n            integerTextField.setOnKeyPressed(event -> {\n                if (isEditing() && event.getCode() == KeyCode.ENTER) {\n                    commitEdit(integerTextField.getInteger());\n                }\n            });\n\n            focusChangeListener = (observable, oldValue, newValue) -> {\n                if (isEditing() && !newValue) {\n                    commitEdit(integerTextField.getInteger());\n                }\n            };\n\n            integerTextField.focusedProperty().addListener(new WeakChangeListener<>(focusChangeListener));\n        }\n\n        return integerTextField;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/LockedCommodityListCell.java",
    "content": "package jgnash.uifx.control;\n\nimport javafx.scene.control.ListCell;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.util.LockedCommodityNode;\n\n/**\n * Provides visual feedback that items are locked and may not be moved.\n *\n * @author Craig Cavanaugh\n */\npublic class LockedCommodityListCell<T extends CommodityNode> extends ListCell<LockedCommodityNode<T>> {\n\n    @Override\n    public void updateItem(final LockedCommodityNode<T> item, final boolean empty) {\n        super.updateItem(item, empty);  // required\n\n        if (!empty) {\n            if (item.isLocked()) {\n                setId(StyleClass.DISABLED_CELL_ID);\n                setDisable(true);\n            } else {\n                setId(StyleClass.ENABLED_CELL_ID);\n                setDisable(false);\n            }\n\n            setText(item.toString());\n        } else {\n            setText(\"\");\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/NullTableViewSelectionModel.java",
    "content": "package jgnash.uifx.control;\n\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TablePosition;\nimport javafx.scene.control.TableView;\n\n/**\n * Disables selection.\n *\n * @author Craig Cavanaugh\n */\npublic class NullTableViewSelectionModel<S> extends TableView.TableViewSelectionModel<S> {\n\n    public NullTableViewSelectionModel(TableView<S> tableView) {\n        super(tableView);\n    }\n\n    @SuppressWarnings(\"rawtypes\")\n\t@Override\n    public ObservableList<TablePosition> getSelectedCells() {\n        return FXCollections.emptyObservableList();\n    }\n\n    @Override\n    public void clearSelection(int row, TableColumn<S,?> column) {\n\n    }\n\n    @Override\n    public void clearAndSelect(int row, TableColumn<S,?> column) {\n\n    }\n\n    @Override\n    public void select(int row, TableColumn<S,?> column) {\n\n    }\n\n\n\n    @Override\n    public boolean isSelected(int row, TableColumn<S,?> column) {\n        return false;\n    }\n\n    @Override\n    public void selectLeftCell() {\n\n    }\n\n    @Override\n    public void selectRightCell() {\n\n    }\n\n    @Override\n    public void selectAboveCell() {\n\n    }\n\n    @Override\n    public void selectBelowCell() {\n\n    }\n\n    public void selectIndices(int row, int... rows) {\n\n    }\n\n    @Override public void selectAll() {\n\n    }\n\n    @Override public void clearSelection(int index) {\n\n    }\n\n    @Override public void clearSelection() {\n\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/PopOverButton.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.control.MenuButton;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.layout.VBox;\n\nimport jgnash.uifx.skin.StyleClass;\n\n/**\n * Pop Over Button control\n *\n * @author Craig Cavanaugh\n */\npublic class PopOverButton extends MenuButton {\n\n    @SuppressWarnings(\"unused\")\n    public PopOverButton() {\n        this(null);\n    }\n\n    public PopOverButton(final Node graphic) {\n        super(null, graphic, (MenuItem[])null);\n\n        getStyleClass().add(StyleClass.POP_OVER_BUTTON);\n    }\n\n    private final ObjectProperty<Node> contentNode = new SimpleObjectProperty<>(this, \"contentNode\");\n\n    /**\n     * Returns the value of the content property\n     *\n     * @return the content node\n     */\n    @SuppressWarnings(\"unused\")\n    public final Node getContentNode() {\n        return contentNode.get();\n    }\n\n    /**\n     * Sets the value of the content property.\n     *\n     * @param content the new content node value\n     */\n    public final void setContentNode(final Node content) {\n        contentNode.set(content);\n\n        final VBox vBox = new VBox(5);\n        vBox.setAlignment(Pos.CENTER);\n        vBox.getChildren().setAll(content);\n\n        final MenuItem menuItem = new MenuItem();\n        menuItem.setGraphic(vBox);\n\n        getItems().setAll(menuItem);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/QuoteSourceComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.engine.QuoteSource;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * ComboBox that allows selection of a {@code QuoteSource}.\n *\n * @author Craig Cavanaugh\n */\npublic class QuoteSourceComboBox extends ComboBox<QuoteSource> {\n\n    public QuoteSourceComboBox() {\n        setEditable(false);\n\n        JavaFXUtils.runLater(this::loadModel); // lazy load to let the ui build happen faster\n    }\n\n    private void loadModel() {\n        // extract and reuse the default model\n        final ObservableList<QuoteSource> items = getItems();\n\n        // warp in a sorted list\n        setItems(new SortedList<>(items, null));\n\n        items.addAll(QuoteSource.values());\n\n        setValue(QuoteSource.YAHOO);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/SecurityComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * ComboBox that allows selection of a SecurityNode and manages it's own model.\n * <p>\n * The default operation is to load all known {@code SecurityNodes}.  If the\n * {@code accountProperty} is set, then only the account's {@code SecurityNodes}\n * will be available for selection.\n *\n * @author Craig Cavanaugh\n */\npublic class SecurityComboBox extends ComboBox<SecurityNode> implements MessageListener {\n\n    /**\n     * Model for the ComboBox.\n     */\n    final private ObservableList<SecurityNode> items;\n\n    final private ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    public SecurityComboBox() {\n\n        // extract and reuse the default model\n        items = getItems();\n\n        // warp in a sorted list\n        setItems(new SortedList<>(items, null));\n\n        JavaFXUtils.runLater(this::loadModel); // lazy load to let the ui build happen faster\n\n        account.addListener((observable, oldValue, newValue) -> loadModel());\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.ACCOUNT, MessageChannel.COMMODITY, MessageChannel.SYSTEM);\n    }\n\n    public void setSecurityNode(final SecurityNode securityNode) {\n        JavaFXUtils.runLater(() -> setValue(securityNode));\n    }\n\n    private void loadModel() {\n        final Collection<SecurityNode> securityNodes;\n\n        if (account.get() != null) {\n            items.clear();\n            securityNodes = account.get().getSecurities();\n        } else {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n            securityNodes = engine.getSecurities();\n        }\n\n        if (!securityNodes.isEmpty()) {\n            final List<SecurityNode> sortedNodeList = new ArrayList<>(securityNodes);\n            Collections.sort(sortedNodeList);\n\n            items.addAll(sortedNodeList);\n            getSelectionModel().select(0);\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        if (event.getObject(MessageProperty.COMMODITY) instanceof SecurityNode) {\n\n            final SecurityNode node = event.getObject(MessageProperty.COMMODITY);\n            final Account account = event.getObject(MessageProperty.ACCOUNT);\n\n            JavaFXUtils.runLater(() -> {\n                switch (event.getEvent()) {\n                    case ACCOUNT_SECURITY_ADD:\n                        if (account != null && account.equals(this.account.get())) {\n                            final int index = Collections.binarySearch(items, node);\n\n                            if (index < 0) {\n                                items.add(-index -1, node);\n                            }\n                        }\n                        break;\n                    case ACCOUNT_SECURITY_REMOVE:\n                        if (account != null && account.equals(this.account.get())) {\n                            items.removeAll(node);\n                        }\n                        break;\n                    case SECURITY_REMOVE:\n                        items.removeAll(node);\n                        break;\n                    case SECURITY_ADD:\n                        final int index = Collections.binarySearch(items, node);\n\n                        if (index < 0) {\n                            items.add(-index -1, node);\n                        }\n                        break;\n                    case SECURITY_MODIFY:\n                        items.removeAll(node);\n\n                        final int i = Collections.binarySearch(items, node);\n\n                        if (i < 0) {\n                            items.add(-i - 1, node);\n                        }\n                        break;\n                    case FILE_CLOSING:\n                        items.clear();\n                    default:\n                        break;\n                }\n            });\n        }\n    }\n\n    public ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/SecurityHistoryEventTypeComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.engine.SecurityHistoryEventType;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * ComboBox that allows selection of a {@code SecurityHistoryEventType}.\n *\n * @author Craig Cavanaugh\n */\npublic class SecurityHistoryEventTypeComboBox extends ComboBox<SecurityHistoryEventType> {\n\n    public SecurityHistoryEventTypeComboBox() {\n        setEditable(false);\n\n        JavaFXUtils.runLater(this::loadModel); // lazy load to let the ui build happen faster\n    }\n\n    private void loadModel() {\n        // extract and reuse the default model\n        ObservableList<SecurityHistoryEventType> items = getItems();\n\n        // warp in a sorted list\n        setItems(new SortedList<>(items, null));\n\n        items.addAll(SecurityHistoryEventType.DIVIDEND, SecurityHistoryEventType.SPLIT);\n\n        setValue(SecurityHistoryEventType.DIVIDEND);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/SecurityNodeAreaChart.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.List;\nimport java.util.Optional;\n\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.chart.AreaChart;\nimport javafx.scene.chart.NumberAxis;\nimport javafx.util.StringConverter;\n\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Extends and AreaChart to encapsulate generation of a SecurityNode chart.\n *\n * @author Craig Cavanaugh\n */\npublic class SecurityNodeAreaChart extends AreaChart<Number, Number> {\n\n    private static final int TICK_MARKS = 14;\n\n    private final SimpleObjectProperty<SecurityNode> securityNodeProperty = new SimpleObjectProperty<>();\n\n    private final NumberAxis xAxis;\n\n    public SecurityNodeAreaChart() {\n        super(new NumberAxis(), new NumberAxis());\n\n        setCreateSymbols(false);\n        setLegendVisible(false);\n        animatedProperty().bind(Options.animationsEnabledProperty());\n\n        xAxis = (NumberAxis) getXAxis();\n\n        xAxis.setTickLabelFormatter(new NumberDateStringConverter());\n\n        xAxis.tickLabelRotationProperty().set(-60);\n        xAxis.setAutoRanging(false);\n\n        securityNodeProperty().addListener((observable, oldValue, newValue) -> update());\n    }\n\n    public SimpleObjectProperty<SecurityNode> securityNodeProperty() {\n        return securityNodeProperty;\n    }\n\n    public void update() {\n        JavaFXUtils.runLater(getData()::clear); // clear old data\n\n        new Thread(() -> {\n            final SecurityNode securityNode = securityNodeProperty().get();\n\n            if (securityNode != null) { // protect against an NPE caused by security rename\n                final Optional<LocalDate[]> optional = securityNode.getLocalDateBounds();\n\n                optional.ifPresent(localDates -> {\n                    final List<List<SecurityHistoryNode>> groups = securityNode.getHistoryNodeGroupsBySplits();\n\n                    for (int i = 0; i < groups.size(); i++) {\n                        final Series<Number, Number> series = new Series<>();\n                        series.setName(securityNode.getSymbol() + i);\n\n                        for (final SecurityHistoryNode node : groups.get(i)) {\n                            series.getData().add(new Data<>(node.getLocalDate().toEpochDay(), node.getAdjustedPrice()));\n                        }\n\n                        JavaFXUtils.runLater(() -> getData().add(series));\n                    }\n\n                    xAxis.setLowerBound(localDates[0].toEpochDay());\n                    xAxis.setUpperBound(localDates[1].toEpochDay());\n\n                    final long range = localDates[1].toEpochDay() - localDates[0].toEpochDay();\n\n                    if (range > TICK_MARKS) {\n                        xAxis.setTickUnit((int)((localDates[1].toEpochDay() - localDates[0].toEpochDay()) / TICK_MARKS));\n                    } else {\n                        xAxis.setTickUnit(range - 1);\n                    }\n                });\n            }\n        }).start();\n    }\n\n    private static class NumberDateStringConverter extends StringConverter<Number> {\n\n        final DateTimeFormatter formatter = DateUtils.getShortDateFormatter();\n\n        @Override\n        public String toString(final Number value) {\n            return formatter.format(LocalDate.ofEpochDay(value.longValue()));\n        }\n\n        @Override\n        public Number fromString(final String string) {\n            return LocalDate.parse(string, formatter).toEpochDay();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/ShortDateTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.uifx.control;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\n\nimport javafx.scene.control.TableCell;\n\nimport jgnash.time.DateUtils;\n\n/**\n * {@code TableCell} for {@code Dates} formatted in a simplified manner.\n *\n * @author Craig Cavanaugh\n */\npublic class ShortDateTableCell<S> extends TableCell<S, LocalDate> {\n\n    private final DateTimeFormatter dateTimeFormatter = DateUtils.getShortDateFormatter();\n\n    @Override\n    protected void updateItem(final LocalDate date, final boolean empty) {\n        super.updateItem(date, empty);  // required\n\n        if (!empty && date != null) {\n            setText(dateTimeFormatter.format(date));\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/StatusBar.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.scene.Node;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ProgressIndicator;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.StackPane;\n\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Status bar.\n *\n * @author Craig Cavanaugh\n */\npublic class StatusBar extends StackPane {\n\n    private final StringProperty text = new SimpleStringProperty(this, \"text\");\n\n    private final ObjectProperty<Node> graphic = new SimpleObjectProperty<>(this, \"graphic\");\n\n    private final DoubleProperty progress = new SimpleDoubleProperty(this, \"progress\");\n\n    public StatusBar() {\n        getStyleClass().add(\"status-bar\");\n\n        final Label label = new Label();\n        label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);\n        label.textProperty().bind(textProperty());\n        label.graphicProperty().bind(graphicProperty());\n        label.getStyleClass().add(\"status-label\");\n        label.textFillProperty().bind(ThemeManager.controlTextFillProperty());\n\n        textProperty().set(ResourceUtils.getString(\"Button.Ok\"));\n\n        final ProgressIndicator progressBar = new ProgressIndicator();\n        progressBar.progressProperty().bind(progressProperty());\n        progressBar.visibleProperty().bind(Bindings.notEqual(0, progressProperty()));\n        progressBar.maxHeightProperty().bind(heightProperty());\n\n        final BorderPane borderPane = new BorderPane();\n        borderPane.setCenter(label);\n        borderPane.setRight(progressBar);\n\n        getChildren().add(borderPane);\n    }\n\n    /**\n     * The property used for storing the text message shown by the status bar.\n     *\n     * @return the text message property\n     */\n    public final StringProperty textProperty() {\n        return text;\n    }\n\n    /**\n     * The property used to store a graphic node that can be displayed by the\n     * status label inside the status bar control.\n     *\n     * @return the property used for storing a graphic node\n     */\n    public final ObjectProperty<Node> graphicProperty() {\n        return graphic;\n    }\n\n    /**\n     * The property used to store the progress, a value between 0 and 1. A negative\n     * value causes the progress indicator to show an indeterminate state.\n     *\n     * @return the property used to store the progress of a task\n     */\n    public final DoubleProperty progressProperty() {\n        return progress;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/TabViewPane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.io.IOException;\n\nimport javafx.fxml.FXMLLoader;\nimport javafx.geometry.Side;\nimport javafx.scene.Node;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.BorderPane;\n\n/**\n * Extended TabPane for consistent view creation.\n *\n * @author Craig Cavanaugh\n */\npublic class TabViewPane extends TabPane {\n\n    private static final String VIEW_TITLE = \"view-title\";\n\n    public TabViewPane() {\n        FXMLLoader loader = new FXMLLoader(getClass().getResource(\"TabViewPane.fxml\"));\n        loader.setRoot(this);\n        loader.setController(this);\n\n        try {\n            loader.load();\n        } catch (final IOException exception) {\n            throw new RuntimeException(exception);\n        }\n\n        setSide(Side.LEFT);\n        setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);\n    }\n\n    public void addTab(final Node node, final String description) {\n        BorderPane borderPane = new BorderPane();\n        TitledPane titledPane = new TitledPane(description, null);\n        titledPane.setCollapsible(false);\n        titledPane.setExpanded(false);\n        titledPane.setFocusTraversable(false);\n\n        titledPane.getStyleClass().add(VIEW_TITLE);\n\n        borderPane.setTop(titledPane);\n        borderPane.setCenter(node);\n\n        Tab tab = new Tab();\n        tab.setText(description);\n        tab.setContent(borderPane);\n\n        getTabs().add(tab);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/TableViewEx.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.input.Clipboard;\nimport javafx.scene.input.ClipboardContent;\nimport javafx.scene.input.KeyCode;\n\nimport jgnash.time.DateUtils;\n\n/**\n * Expanded TableView with a default Copy to clipboard handler\n *\n * @param <S> The type of the objects contained within the TableView items list.\n *\n * @author Craig Cavanaugh\n */\npublic class TableViewEx<S> extends TableView<S> {\n\n    private Function<S, String> clipBoardStringFunction = null;\n\n    public TableViewEx() {\n        super();\n\n        // register a keyboard copy command for transactions\n        setOnKeyPressed(event -> {\n            if (event.isShortcutDown() && event.getCode() == KeyCode.C) {\n                if (clipBoardStringFunction != null) {\n                    handleCopyToClipboard();\n                } else {\n                    handleGenericCopyToClipboard();\n                }\n                event.consume();\n            }\n        });\n    }\n\n    public void handleCopyToClipboard() {\n        final List<S> items = getSelectionModel().getSelectedItems();\n\n        if (items.size() > 0 && getClipBoardStringFunction() != null) {\n            final Clipboard clipboard = Clipboard.getSystemClipboard();\n            final ClipboardContent content = new ClipboardContent();\n            final StringBuilder builder = new StringBuilder();\n\n            for (final S item : items) {\n                builder.append(getClipBoardStringFunction().apply(item));\n                builder.append('\\n');\n            }\n\n            content.putString(builder.toString());\n            clipboard.setContent(content);\n        }\n    }\n\n    @SuppressWarnings(\"WeakerAccess\")\n    public Function<S, String> getClipBoardStringFunction() {\n        return clipBoardStringFunction;\n    }\n\n    public void setClipBoardStringFunction(final Function<S, String> clipBoardStringFunction) {\n        this.clipBoardStringFunction = clipBoardStringFunction;\n    }\n\n    private void handleGenericCopyToClipboard() {\n        final List<Integer> integerList = getSelectionModel().getSelectedIndices();\n\n        if (integerList.size() > 0) {\n            final Clipboard clipboard = Clipboard.getSystemClipboard();\n            final ClipboardContent content = new ClipboardContent();\n            final StringBuilder builder = new StringBuilder();\n\n            for (final Integer row : integerList) {\n                final List<TableColumn<S, ?>> columns = getColumns();\n                for (int i = 0; i < columns.size(); i++) {\n                    final TableColumn<S, ?> column = columns.get(i);\n\n                    if (column.getCellObservableValue(row) != null) {\n                        final Object value = column.getCellObservableValue(row).getValue();\n\n                        if (value != null) {\n                            if (value instanceof BigDecimal) {\n                                builder.append(((BigDecimal) value).toPlainString());\n                            } else if (value instanceof LocalDate) {\n                                builder.append(DateUtils.getExcelDateFormatter().format((LocalDate) value));\n                            } else {\n                                builder.append(value);\n                            }\n                        }\n                    }\n                    if (i < columns.size() - i) {\n                        builder.append('\\t');\n                    }\n                }\n                builder.append('\\n');\n            }\n            content.putString(builder.toString());\n            clipboard.setContent(content);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/TextFieldEx.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.scene.control.TextField;\n\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * TextField with expanded capabilities.\n *\n * @author Craig Cavanaugh\n */\npublic class TextFieldEx extends TextField {\n\n    private static final BooleanProperty selectOnFocus;\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<Boolean> focusChangeListener;\n\n    static {\n        selectOnFocus = new SimpleBooleanProperty();\n        selectOnFocus.bind(Options.selectOnFocusProperty());\n    }\n\n    public TextFieldEx() {\n        // If the select on focus property is enabled, select the text when focus is received\n        focusChangeListener = (observable, oldValue, newValue) -> {\n            if (selectOnFocus.get()) {\n                JavaFXUtils.runLater(() -> {\n                    if (getText() != null && isFocused() && isEditable() && !getText().isEmpty()) {\n                        selectAll();\n                    }\n                });\n            }\n        };\n\n        focusedProperty().addListener(new WeakChangeListener<>(focusChangeListener));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/TextInputDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.NamedArg;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.Label;\nimport javafx.stage.Stage;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * A better behaved TextInputDialog.\n *\n * @author Craig Cavanaugh\n */\npublic class TextInputDialog {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private Button okButton;\n\n    @FXML\n    private Button cancelButton;\n\n    @FXML\n    private TextFieldEx textField;\n\n    @FXML\n    private Label message;\n\n    private final Stage dialog;\n\n    public TextInputDialog(@NamedArg(\"defaultValue\") final String defaultValue) {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        dialog = FXMLUtils.loadFXML(this, \"TextInputDialog.fxml\", resources);\n\n        JavaFXUtils.runLater(() -> textField.setText(defaultValue));\n\n        setGraphic(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.QUESTION_CIRCLE,\n                ThemeManager.getBaseTextHeight() * Alert.HEIGHT_MULTIPLIER));\n    }\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        okButton.setOnAction(event -> handleOkayAction());\n        cancelButton.setOnAction(event -> handleCancelAction());\n        okButton.disableProperty().bind(textField.textProperty().isEmpty());\n    }\n\n    public void setTitle(final String title) {\n        dialog.setTitle(title);\n    }\n\n    public void setContentText(final String contentText) {\n        message.setText(contentText);\n    }\n\n    private void setGraphic(final Node node) {\n        message.setGraphic(node);\n    }\n\n    public Optional<String> showAndWait() {\n        dialog.sizeToScene();\n        dialog.setResizable(false);\n        dialog.showAndWait();\n        return Optional.ofNullable(textField.getText());\n    }\n\n    private void handleOkayAction() {\n        ((Stage)parent.get().getWindow()).close();\n    }\n\n    private void handleCancelAction() {\n        textField.setText(null);\n        ((Stage)parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/TimePeriodComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport javafx.beans.property.ReadOnlyIntegerProperty;\nimport javafx.beans.property.ReadOnlyIntegerWrapper;\nimport javafx.scene.control.ComboBox;\nimport jgnash.resource.util.ResourceUtils;\n\nimport java.util.ResourceBundle;\n\n/**\n * ComboBox that allows for selection of predetermined periods of times.\n * <p>\n * The period returned is in milliseconds.  getPeriods and getDescriptions\n * can be overridden to change the available periods\n *\n * @author Craig Cavanaugh\n */\npublic class TimePeriodComboBox extends ComboBox<String> {\n\n    private int[] periods = new int[0];\n\n    private final ReadOnlyIntegerWrapper period = new ReadOnlyIntegerWrapper();\n\n    public TimePeriodComboBox() {\n        loadModel();\n\n        // Update the period property automatically\n        valueProperty().addListener((observable, oldValue, newValue)\n                -> period.setValue(periods[getSelectionModel().getSelectedIndex()]));\n    }\n\n    public ReadOnlyIntegerProperty periodProperty() {\n        return period.getReadOnlyProperty();\n    }\n\n    private void loadModel() {\n        periods = getPeriods();\n        String[] descriptions = getDescriptions();\n\n        assert periods.length == descriptions.length;\n\n        getItems().addAll(getDescriptions());\n    }\n\n    /**\n     * Sets the selected period.  Period must be a valid period\n     * or no change will occur.\n     *\n     * @param period period in milliseconds to select\n     */\n    public void setSelectedPeriod(final int period) {\n        for (int i = 0; i < periods.length; i++) {\n            if (period == periods[i]) {\n                getSelectionModel().select(i);\n                break;\n            }\n        }\n    }\n\n    public static int[] getPeriods() {\n        // only non-zero values are allowed\n        return new int[]{300000, 600000, 900000, 1800000, 3600000, 7200000, 28800000, 86400000, 1};\n    }\n\n    private static String[] getDescriptions() {\n\n        final ResourceBundle rb = ResourceUtils.getBundle();\n\n        return new String[]{rb.getString(\"Period.5Min\"), rb.getString(\"Period.10Min\"), rb.getString(\"Period.15Min\"),\n                rb.getString(\"Period.30Min\"), rb.getString(\"Period.1Hr\"), rb.getString(\"Period.2Hr\"),\n                rb.getString(\"Period.8Hr\"), rb.getString(\"Period.1Day\"), rb.getString(\"Period.NextStart\")};\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/TransactionNumberComboBox.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control;\n\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Enhanced ComboBox; provides a UI to the user for entering transaction\n * numbers in a consistent manner.\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionNumberComboBox extends ComboBox<String> {\n\n    private final String nextNumberItem;\n\n    final private ObjectProperty<Account> accountProperty = new SimpleObjectProperty<>();\n\n    public TransactionNumberComboBox() {\n        super();\n\n        ResourceBundle rb = ResourceUtils.getBundle();\n\n        nextNumberItem = rb.getString(\"Item.NextNum\");\n        String[] defaultItems = new String[]{\"\", nextNumberItem};\n\n        getItems().addAll(defaultItems);\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        getItems().addAll(engine.getTransactionNumberList());\n\n        setEditable(true);\n\n        valueProperty().addListener((observable, oldValue, newValue) -> new Thread(() -> {\n            if (nextNumberItem.equals(newValue)) {\n                final Account account = accountProperty().getValue();\n\n                if (account != null) {\n                   JavaFXUtils.runLater(() -> setValue(account.getNextTransactionNumber()));\n                }\n            }\n        }).start());\n\n        setOnKeyReleased((final KeyEvent event) -> {\n            if (event.getCode() == KeyCode.DOWN) {\n                getSelectionModel().selectNext();\n            } else if (event.getCode() == KeyCode.UP) {\n                getSelectionModel().selectPrevious();\n            }\n        });\n    }\n\n    public ObjectProperty<Account> accountProperty() {\n        return accountProperty;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/autocomplete/AutoCompleteFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.autocomplete;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AutoCompleteTextField;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.MultiHashMap;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * This factory class generates AutoCompleteTextFields that share a common model\n * to reduce the amount of overhead.\n *\n * @author Craig Cavanaugh\n * @author Don Brown\n */\npublic class AutoCompleteFactory {\n\n    private static MemoModel memoModel;\n\n    private static final Object synchronizationObject = new Object();\n\n    /**\n     * Use an ExecutorService to manage the number of running threads.\n     */\n    private static final ExecutorService pool\n            = Executors.newSingleThreadExecutor(\n            new DefaultDaemonThreadFactory(\"Auto Complete Factory Executor\"));\n\n    private AutoCompleteFactory() {\n        // Factory class\n    }\n\n    /**\n     * Sets the {@code AutoCompleteModel} for {@code Transaction} memos to\n     * an {@code AutoCompleteTextField<Transaction>}.\n     *\n     * @param autoCompleteTextField text field to bind to\n     */\n    public static void setMemoModel(final AutoCompleteTextField<Transaction> autoCompleteTextField) {\n        if (Options.useAutoCompleteProperty().get()) {\n            synchronized (synchronizationObject) {\n                if (memoModel == null) {\n                    memoModel = new MemoModel();\n                }\n            }\n            autoCompleteTextField.autoCompleteModelObjectProperty().set(memoModel);\n        }\n    }\n\n    /**\n     * Sets the {@code AutoCompleteModel} for {@code Transaction} payees to\n     * an {@code AutoCompleteTextField<Transaction>}.\n     *\n     * @param account               base Account for payee model\n     * @param autoCompleteTextField text Field to bind to\n     */\n    public static void setPayeeModel(final AutoCompleteTextField<Transaction> autoCompleteTextField, final Account account) {\n        if (Options.useAutoCompleteProperty().get()) {\n            autoCompleteTextField.autoCompleteModelObjectProperty().set(new PayeeAccountModel(account));\n        }\n    }\n\n    private static abstract class TransactionModel extends DefaultAutoCompleteModel<Transaction> implements MessageListener {\n\n        volatile boolean load = false;\n\n        TransactionModel() {\n            init();\n        }\n\n        final void init() {\n            MessageBus.getInstance().registerListener(this, MessageChannel.TRANSACTION, MessageChannel.SYSTEM);\n            load();\n        }\n\n        @Override\n        public void messagePosted(final Message event) {\n            switch (event.getEvent()) {\n                case TRANSACTION_ADD:\n                    Transaction t = event.getObject(MessageProperty.TRANSACTION);\n                    load(t);\n                    return;\n                case FILE_LOAD_SUCCESS:\n                    reload();\n                    return;\n                case FILE_CLOSING:\n                    load = false; // prevent loading\n                    purge();\n                    return;\n                default:\n            }\n        }\n\n        void load() {\n            load = true;\n\n            pool.execute(() -> {\n                try {\n                    final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                    Objects.requireNonNull(engine);\n\n                    final List<Transaction> transactions = engine.getTransactions();\n\n                    // sort the transactions for consistent order\n                    Collections.sort(transactions);\n\n                    for (final Transaction t : transactions) {\n                        if (load) {\n                            load(t);\n                        } else {    // loading has been stopped\n                            return;\n                        }\n                    }\n\n                    super.load();\n                } catch (Exception e) {\n                    Logger.getLogger(TransactionModel.class.getName()).log(Level.INFO, e.getLocalizedMessage(), e);\n                }\n            });\n        }\n\n        final void reload() {\n            purge(); // purge the old\n            load(); // load the new\n        }\n\n        abstract void load(Transaction tran);\n    }\n\n    private static final class MemoModel extends PayeeModel {\n\n        @Override\n        void load(final Transaction tran) {\n            if (tran != null) {\n\n                // Add both memo versions\n                addString(tran.getMemo());\n                addString(tran.getTransactionMemo());\n            }\n        }\n    }\n\n    /**\n     * This model stores the transaction with the payee field value. A new\n     * instance is created for each account and is account specific.\n     */\n    private static final class PayeeAccountModel extends PayeeModel {\n\n        private final Account account;\n\n        PayeeAccountModel(final Account account) {\n            super();\n            this.account = account;\n        }\n\n        @Override\n        void load() {\n\n            // Push the load to the end of the application thread for a lazy init\n            JavaFXUtils.runLater(() -> pool.execute(() -> {\n                if (account != null) {\n                    account.getSortedTransactionList().forEach(this::load);\n                }\n\n                // Signal complete at the very end of the application thread\n                JavaFXUtils.runLater(() -> pool.execute(() -> loadComplete.set(true)));\n            }));\n        }\n\n        @Override\n        public void messagePosted(final Message event) {\n            Account a = event.getObject(MessageProperty.ACCOUNT);\n            Transaction t = event.getObject(MessageProperty.TRANSACTION);\n\n            switch (event.getEvent()) {\n                case TRANSACTION_ADD:\n                    if (a.equals(account)) {\n                        load(t);\n                    }\n                    return;\n                case FILE_LOAD_SUCCESS:\n                    reload();\n                    return;\n                case TRANSACTION_REMOVE:\n                    if (a.equals(account)) {\n                        removeExtraInfo(t);\n                    }\n                    return;\n                default:\n            }\n        }\n    }\n\n    /**\n     * This model stores the transaction with the payee field value.\n     * <p>\n     * Split entries are excluded because duplicating one in a form would\n     * only impact the parent split transaction.\n     */\n    private static class PayeeModel extends TransactionModel {\n\n        final MultiHashMap<String, Transaction> transactions = new MultiHashMap<>();\n\n        @Override\n        void load(final Transaction tran) {\n            if (tran != null && tran.getTransactionType() != TransactionType.SPLITENTRY) {\n\n                addString(tran.getPayee());\n\n                if (ignoreCaseEnabled.get()) {\n                    transactions.put(tran.getPayee().toLowerCase(Locale.getDefault()), tran);\n                } else {\n                    transactions.put(tran.getPayee(), tran);\n                }\n            }\n        }\n\n        @Override\n        public Collection<Transaction> getAllExtraInfo(final String key) {\n            if (ignoreCaseEnabled.get()) {\n                return transactions.getAll(key.toLowerCase(Locale.getDefault()));\n            }\n            return transactions.getAll(key);\n        }\n\n        /**\n         * Removes the transaction associated with the payee. This is done so\n         * that deleted transactions can be garbage collected.\n         *\n         * @param t transaction to remove\n         */\n        void removeExtraInfo(final Transaction t) {\n            pool.execute(() -> {\n                if (ignoreCaseEnabled.get()) {\n                    if (!transactions.removeValue(t.getPayee().toLowerCase(Locale.getDefault()), t)) {\n                        Logger.getLogger(AutoCompleteFactory.class.getName())\n                                .finest(ResourceUtils.getString(\"Message.Warn.FailedTransInfoRemoval\"));\n                    }\n                } else {\n                    if (!transactions.removeValue(t.getPayee(), t)) {\n                        Logger.getLogger(AutoCompleteFactory.class.getName())\n                                .finest(ResourceUtils.getString(\"Message.Warn.FailedTransInfoRemoval\"));\n                    }\n                }\n            });\n        }\n\n        @Override\n        public void messagePosted(final Message event) {\n            super.messagePosted(event);\n\n            if (event.getEvent() == ChannelEvent.TRANSACTION_REMOVE) {\n                removeExtraInfo(event.getObject(MessageProperty.TRANSACTION));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/autocomplete/AutoCompleteModel.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.autocomplete;\n\nimport java.util.Collection;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Auto complete model interface.\n *\n * @author Craig Cavanaugh\n * @author Don Brown\n */\npublic interface AutoCompleteModel<E> {\n\n    /**\n     * Performs a search to find the best matching\n     * String to the supplied String.  Returns null if nothing\n     * is found\n     *\n     * @param content content to search for\n     * @return string that was found if any\n     */\n    String doLookAhead(String content);\n\n    /**\n     * Returns extra information that might be stored with a\n     * found string returned by doLookAhead().  This information\n     * can be used to populate other fields based on matching the\n     * string key.\n     *\n     * @param key The string key most likely returned from doLookAhead()\n     * @return A collection of objects that would give extra information about the key\n     */\n    Collection<E> getAllExtraInfo(String key);\n\n    /**\n     * Atomic indicating if the initial load of data is complete to support\n     * a full search\n     *\n     * @return AtomicBoolean indicating completion\n     */\n    AtomicBoolean isLoadComplete();\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/autocomplete/DefaultAutoCompleteModel.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.autocomplete;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport javafx.beans.property.SimpleBooleanProperty;\n\nimport jgnash.uifx.Options;\nimport jgnash.util.Nullable;\n\n/**\n * Default model for auto complete search.\n * \n * @author Craig Cavanaugh\n * @author Don Brown\n * @author Pranay Kumar\n */\nabstract class DefaultAutoCompleteModel<E> implements AutoCompleteModel<E> {\n\n    private final List<String> list = new LinkedList<>();\n\n    private final SimpleBooleanProperty autoCompleteEnabled = new SimpleBooleanProperty(true);\n\n    final SimpleBooleanProperty ignoreCaseEnabled = new SimpleBooleanProperty(false);\n\n    private final SimpleBooleanProperty fuzzyMatchEnabled = new SimpleBooleanProperty(false);\n\n    /**\n     * Must be set to true when the initial load has been completed\n     */\n    final AtomicBoolean loadComplete = new AtomicBoolean(false);\n\n    DefaultAutoCompleteModel() {\n\n        // bind preferences to the global options\n        autoCompleteEnabled.bind(Options.useAutoCompleteProperty());\n        ignoreCaseEnabled.bind(Options.autoCompleteIsCaseSensitiveProperty().not());\n        fuzzyMatchEnabled.bind(Options.useFuzzyMatchForAutoCompleteProperty());\n    }\n\n\n    @Override\n    public String doLookAhead(final String content) {\n        if (autoCompleteEnabled.get()) {\n            return doLookAhead(content, ignoreCaseEnabled.get());\n        }\n        return null;\n    }\n\n    /**\n     * Perform a brute force linear search top down for the best match.\n     *\n     * @param content content to search for\n     * @param ignoreCase true is search is case insensitive\n     * @return best match if found, null otherwise\n     */\n    private @Nullable String doLookAhead(final String content, final boolean ignoreCase) {\n        if (!content.isEmpty()) {\n            synchronized (list) {\n                for (final String s : list) {\n\n                    if (ignoreCase) {\n                        if (s.equalsIgnoreCase(content)) {\n                            break;\n                        }\n                    } else {\n                        if (s.equals(content)) {\n                            break;\n                        }\n                    }\n\n                    if (startsWith(s, content, ignoreCase)) {\n                        return s;\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    void addString(final String content) {\n        if (content != null && !content.isEmpty()) {\n            synchronized (list) {\n                if (fuzzyMatchEnabled.get()) {\n                    list.remove(content);       // remove old instance\n                    list.add(0, content); // push it to the top of the search list\n                } else {\n                    int index = Collections.binarySearch(list, content);\n\n                    if (index < 0) {\n                        list.add(-index - 1, content);\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Removes all of the strings that have been remembered.\n     */\n    void purge() {\n        synchronized (list) {\n            list.clear();\n        }\n    }\n\n    void load() {\n        loadComplete.set(true);\n    }\n\n    public final AtomicBoolean isLoadComplete() {\n        return loadComplete;\n    }\n\n    /**\n     * Returns extra information that might be stored with a\n     * found string returned by doLookAhead().  This information\n     * can be used to populate other fields based on matching the\n     * string key.\n     *\n     * @param key The string key most likely returned from doLookAhead()\n     * @return A list of objects that would give extra information about the key\n     */\n    @Override\n    public Collection<E> getAllExtraInfo(final String key) {\n        return Collections.emptyList();\n    }\n\n    /**\n     * Tests if the source string starts with the prefix string. Case is\n     * ignored.\n     * \n     * @param source the source String.\n     * @param prefix the prefix String.\n     * @param ignoreCase true if case should be ignored\n     * @return true, if the source starts with the prefix string.\n     */\n    private static boolean startsWith(final String source, final String prefix, final boolean ignoreCase) {\n        return prefix.length() <= source.length() && source.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/wizard/AbstractWizardPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.wizard;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\n\n/**\n * Support class to make implementation of {@code WizardPaneController} less repetitive.  Subclasses are required\n * to overwrite {@code toString()} and return a meaningful description.  {@code toString()} will be called during\n * class initialization.\n *\n * @author Craig Cavanaugh\n */\npublic abstract class AbstractWizardPaneController<K extends Enum<?>> implements WizardPaneController<K> {\n\n    private final SimpleObjectProperty<WizardDescriptor> descriptorProperty =\n            new SimpleObjectProperty<>(new WizardDescriptor(toString()));\n\n    @Override\n    public ObjectProperty<WizardDescriptor> descriptorProperty() {\n        return descriptorProperty;\n    }\n\n    /**\n     * Should be called when a change to validity occurs.\n     */\n    protected void updateDescriptor() {\n        descriptorProperty().get().setIsValid(isPaneValid());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/wizard/WizardDescriptor.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.wizard;\n\nimport java.util.Objects;\n\nimport jgnash.util.NotNull;\n\n/**\n * Wizard Task Descriptor and status object.\n *\n * @author Craig Cavanaugh\n */\nclass WizardDescriptor {\n\n    private boolean valid;\n\n    private String description = \"\";\n\n    WizardDescriptor(@NotNull final String description) {\n        Objects.requireNonNull(description);\n\n        setDescription(description);\n    }\n\n    boolean isValid() {\n        return valid;\n    }\n\n    void setIsValid(boolean valid) {\n        this.valid = valid;\n    }\n\n    @NotNull\n    public String getDescription() {\n        return description;\n    }\n\n    private void setDescription(@NotNull final String description) {\n        Objects.requireNonNull(description);\n\n        this.description = description;\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (this == o) {\n            return true;\n        }\n\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        WizardDescriptor that = (WizardDescriptor) o;\n        return Objects.equals(getDescription(), that.getDescription());\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(getDescription());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/wizard/WizardDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.wizard;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyBooleanProperty;\nimport javafx.beans.property.ReadOnlyBooleanWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for the wizard dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class WizardDialogController<K extends Enum<?>> {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private TitledPane taskTitlePane;\n\n    @FXML\n    protected ResourceBundle resources;\n\n    @FXML\n    private ListView<WizardDescriptor> taskList;\n\n    @FXML\n    private StackPane taskPane;\n\n    @FXML\n    private Button backButton;\n\n    @FXML\n    private Button nextButton;\n\n    @FXML\n    private Button finishButton;\n\n    @FXML\n    private Button cancelButton;\n\n    private final Map<K, Object> settings = new HashMap<>();\n\n    private final BooleanProperty valid = new SimpleBooleanProperty(false);\n\n    private final IntegerProperty selectedIndex = new SimpleIntegerProperty();\n\n    private final Map<WizardPaneController<K>, Pane> paneMap = new HashMap<>();\n\n    private final DoubleProperty taskListWidth = new SimpleDoubleProperty();\n\n    @FXML\n    private void initialize() {\n        cancelButton.setCancelButton(true);\n        nextButton.setDefaultButton(true);\n\n        taskList.setEditable(false);\n\n        // updates the task title\n        taskList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue)\n                -> handleTaskChange(oldValue, newValue));\n\n        taskList.setCellFactory(param -> new ControllerListCell());\n\n        // bind the min and max width to a value we control.  ListView does a terrible job of controlling it's width\n        taskList.minWidthProperty().bind(taskListWidth);\n        taskList.maxWidthProperty().bind(taskListWidth);\n\n        selectedIndex.bind(taskList.getSelectionModel().selectedIndexProperty());\n    }\n\n    public ReadOnlyBooleanProperty validProperty() {\n        return new ReadOnlyBooleanWrapper(valid.get());\n    }\n\n    public void addTaskPane(final WizardPaneController<K> wizardPaneController, final Pane pane) {\n        Objects.requireNonNull(wizardPaneController);\n        Objects.requireNonNull(pane);\n\n        wizardPaneController.getSettings(settings);\n        wizardPaneController.putSettings(settings);\n\n        // Listen for changes to the controller descriptor\n        wizardPaneController.descriptorProperty().addListener(observable -> taskList.refresh());\n\n        taskList.getItems().add(wizardPaneController.descriptorProperty().get());\n        paneMap.put(wizardPaneController, pane);\n\n        // force selection if this is the first pane\n        if (taskList.getItems().size() == 1) {\n            taskList.getSelectionModel().select(0);\n\n            taskPane.minWidthProperty().bind(pane.minWidthProperty());\n        }\n\n        // update the preferred task list width\n        taskListWidth.set(Math.max(getControllerDescriptionWidth(wizardPaneController), taskListWidth.get()));\n\n        updateButtonState();\n    }\n\n    public Object getSetting(final K key) {\n        return settings.get(key);\n    }\n\n    public void setSetting(final K key, final Object value) {\n        settings.put(key, value);\n\n        /* New setting. Tell each page to read */\n        for (WizardPaneController<K> wizardPaneController : paneMap.keySet()) {\n            wizardPaneController.getSettings(settings);\n        }\n\n        updateButtonState();\n    }\n\n    // handle a task change via the list\n    private void handleTaskChange(final WizardDescriptor oldDescriptor, final WizardDescriptor newDescriptor) {\n\n        // store the settings of the prior controller... the user may be skipping around\n        if (oldDescriptor != null) {\n            getController(oldDescriptor).putSettings(settings);\n        }\n\n        getController(newDescriptor).getSettings(settings);\n\n        taskTitlePane.textProperty().set(newDescriptor.getDescription());\n        updateButtonState();\n\n        paneMap.keySet().stream().filter(controller -> controller.descriptorProperty().get().equals(newDescriptor))\n                .forEach(controller -> {\n            taskPane.getChildren().clear();\n            taskPane.getChildren().addAll(paneMap.get(controller));\n        });\n    }\n\n    private WizardPaneController<K> getController(final WizardDescriptor descriptor) {\n        for (final WizardPaneController<K> wizardPaneController : paneMap.keySet()) {\n            if (wizardPaneController.descriptorProperty().get().equals(descriptor)) {\n                return wizardPaneController;\n            }\n        }\n\n        throw new IllegalArgumentException(\"Did not find a controller match for the descriptor\");\n    }\n\n    private void updateButtonState() {\n        nextButton.setDisable(selectedIndex.get() < 0 || selectedIndex.get() >= taskList.getItems().size() - 1);\n\n        if (selectedIndex.get() == taskList.getItems().size() - 1) {\n            boolean isValid = true;\n\n            for (WizardPaneController<K> wizardPaneController : paneMap.keySet()) {\n                if (!wizardPaneController.isPaneValid()) {\n                    isValid = false;\n                }\n            }\n\n            finishButton.setDisable(!isValid);\n        } else {\n            finishButton.setDisable(true);\n        }\n\n        backButton.setDisable(selectedIndex.get() == 0);\n    }\n\n    @FXML\n    private void handleCancelAction() {\n        valid.set(false);\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleNextAction() {\n        if (selectedIndex.get() < taskList.getItems().size() - 1) {\n            // store an setting on the active page. May be necessary for the next page\n            getController(taskList.getSelectionModel().getSelectedItem()).putSettings(settings);\n\n            // select the next page\n            taskList.getSelectionModel().select(selectedIndex.get() + 1);\n\n            JavaFXUtils.runLater(() -> {\n                // tell the active page to update\n                getController(taskList.getSelectionModel().getSelectedItem()).getSettings(settings);\n            });\n        }\n    }\n\n    @FXML\n    private void handleBackAction() {\n        if (selectedIndex.get() > 0) {\n\n            // select the previous page\n            taskList.getSelectionModel().select(selectedIndex.get() - 1);\n        }\n    }\n\n    @FXML\n    private void handleFinishAction() {\n        valid.set(true);\n\n        for (WizardPaneController<K> wizardPaneController : paneMap.keySet()) {\n            if (!wizardPaneController.isPaneValid()) {\n                valid.set(false);\n                break;\n            }\n            wizardPaneController.putSettings(settings);\n        }\n\n        if (valid.get()) {\n            ((Stage) parent.get().getWindow()).close();\n        }\n    }\n\n    /**\n     * Determines the preferred width of the {@code ListCell} for a better visual width of the task list.\n     *\n     * @param item controller we need the optimal width off\n     * @return preferred width\n     */\n    private double getControllerDescriptionWidth(final WizardPaneController<K> item) {\n        final ControllerListCell cell = new ControllerListCell();\n        cell.updateItem(item.descriptorProperty().get(), false);\n\n        return cell.prefWidth(-1);\n    }\n\n    /**\n     * Custom list cell.  Marks any bad pages with a font change\n     */\n    private class ControllerListCell extends ListCell<WizardDescriptor> {\n\n        ControllerListCell() {\n            super();\n            updateListView(taskList);\n            setSkin(createDefaultSkin());\n        }\n\n        @Override\n        protected void updateItem(final WizardDescriptor item, final boolean empty) {\n            super.updateItem(item, empty);\n            if (!empty) {\n                setText(item.getDescription());\n\n                if (!item.isValid()) {\n                    setId(StyleClass.NORMAL_NEGATIVE_CELL_ID);\n                } else {\n                    setId(StyleClass.NORMAL_CELL_ID);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/control/wizard/WizardPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.wizard;\n\nimport java.util.Map;\n\nimport javafx.beans.property.ObjectProperty;\n\n/**\n * Interface for a task pane in a {@code WizardPane}.\n *\n * @author Craig Cavanaugh\n */\npublic interface WizardPaneController<K extends Enum<?>> {\n\n    default boolean isPaneValid() {\n        return true;\n    }\n\n    /**\n     * Called after a page has been made active.  The page\n     * can load predefined settings/preferences when called.\n     *\n     * @param map preferences are accessible here\n     */\n    default void getSettings(final Map<K, Object> map) {\n\n    }\n\n    /**\n     * Called on the active prior to switching to the next page.  The page\n     * can save settings/preferences to be used later\n     *\n     * @param map place to put default preferences\n     */\n   default void putSettings(final Map<K, Object> map) {\n\n   }\n\n    ObjectProperty<WizardDescriptor> descriptorProperty();\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/ChangeDatabasePasswordDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog;\n\nimport java.io.File;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.PasswordField;\nimport javafx.scene.control.TextField;\nimport javafx.stage.FileChooser;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.jpa.SqlUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.FileChooserFactory;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * Utility Dialog/Controller for changing the password of a relational database.\n *\n * @author Craig Cavanaugh\n */\npublic class ChangeDatabasePasswordDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button okayButton;\n\n    @FXML\n    private TextField databaseTextField;\n\n    @FXML\n    private PasswordField passwordField;\n\n    @FXML\n    private PasswordField newPasswordField;\n\n    @FXML\n    private PasswordField verifyPasswordField;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    void initialize() {\n        okayButton.disableProperty().bind(Bindings.notEqual(newPasswordField.textProperty(),\n                verifyPasswordField.textProperty()).or(Bindings.isEmpty(databaseTextField.textProperty())));\n    }\n\n    @FXML\n    private void handleOkAction() {\n        ((Stage) parent.get().getWindow()).close();\n\n        final boolean result = SqlUtils.changePassword(databaseTextField.getText(),\n                passwordField.getText().toCharArray(), newPasswordField.getText().toCharArray());\n\n        JavaFXUtils.runLater(() -> {\n            if (result) {\n                StaticUIMethods.displayMessage(resources.getString(\"Message.CredentialChange\"));\n            } else {\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.CredentialChange\"));\n            }\n        });\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleDatabaseButtonAction() {\n        final FileChooser fileChooser = FileChooserFactory.getDataStoreChooser(DataStoreType.H2_DATABASE,\n                DataStoreType.HSQL_DATABASE);\n\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null && file.exists()) {\n            databaseTextField.setText(file.getAbsolutePath());\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/ImportScriptsDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.function.Consumer;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.cell.CheckBoxTableCell;\nimport javafx.stage.Stage;\n\nimport jgnash.convert.importat.ImportFilter;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.TableViewManager;\n\n/**\n * Controller for managing import scripts\n *\n * @author Craig Cavanaugh\n */\npublic class ImportScriptsDialogController {\n\n    private static final String PREF_NODE = \"/jgnash/uifx/dialog/ImportScriptsDialogController\";\n\n    private static final double[] PREF_COLUMN_WEIGHTS = {0, 50, 50};\n\n    private static final String DEFAULT = \"default\";\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private Button upButton;\n\n    @FXML\n    private Button downButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private TableView<Script> tableView;\n\n    private final IntegerProperty scriptCountProperty = new SimpleIntegerProperty();\n\n    private final IntegerProperty selectedIndexProperty = new SimpleIntegerProperty();\n\n    private final ObjectProperty<Consumer<List<ImportFilter>>> acceptanceConsumerProperty = new SimpleObjectProperty<>();\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private TableViewManager<Script> tableViewManager;\n\n    @FXML\n    private void initialize() {\n\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        // simplify binding process\n        scriptCountProperty.bind(Bindings.size(tableView.getItems()));\n        selectedIndexProperty.bind(tableView.selectionModelProperty().get().selectedIndexProperty());\n\n        final TableColumn<Script, Boolean> enabledColumn = new TableColumn<>(resources.getString(\"Column.Enabled\"));\n        enabledColumn.setCellValueFactory(param -> param.getValue().enabledProperty);\n        enabledColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enabledColumn));\n\n        enabledColumn.setEditable(true);\n        tableView.getColumns().add(enabledColumn);\n\n        final TableColumn<Script, String> descriptionColumn = new TableColumn<>(resources.getString(\"Column.Description\"));\n        descriptionColumn.setCellValueFactory(param -> param.getValue().descriptionProperty);\n        tableView.getColumns().add(descriptionColumn);\n\n        final TableColumn<Script, String> scriptColumn = new TableColumn<>(resources.getString(\"Column.Script\"));\n        scriptColumn.setCellValueFactory(param -> param.getValue().scriptProperty);\n        tableView.getColumns().add(scriptColumn);\n\n        tableView.setEditable(true);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        tableView.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS);\n\n        upButton.disableProperty().bind(scriptCountProperty.lessThan(2)\n                .or(selectedIndexProperty.lessThan(1)));\n\n        downButton.disableProperty().bind(scriptCountProperty.lessThan(2)\n                .or(selectedIndexProperty.isEqualTo(scriptCountProperty.subtract(1)))\n                .or(selectedIndexProperty.lessThan(0)));\n\n\n        tableViewManager = new TableViewManager<>(tableView, PREF_NODE);\n        tableViewManager.setColumnWeightFactory(column -> PREF_COLUMN_WEIGHTS[column]);\n        tableViewManager.setPreferenceKeyFactory(() -> DEFAULT);\n\n        JavaFXUtils.runLater(tableViewManager::restoreLayout);\n        JavaFXUtils.runLater(tableViewManager::packTable);\n    }\n\n    /**\n     * Loads remain scripts that have not been enabled\n     */\n    private void loadScripts() {\n        for (final ImportFilter importFilter : ImportFilter.getImportFilters()) {\n\n            boolean exists = false;\n\n            for (Script script : tableView.getItems()) {\n                if (script.scriptProperty.get().equals(importFilter.getScript())) {\n                    exists = true;\n                    break;\n                }\n            }\n\n            if (!exists) {\n                final Script script = new Script();\n\n                script.descriptionProperty.setValue(importFilter.getDescription());\n                script.scriptProperty.setValue(importFilter.getScript());\n                script.importFilter = importFilter;\n\n                tableView.getItems().add(script);\n            }\n        }\n    }\n\n    public void setEnabledScripts(final List<ImportFilter> importFilters) {\n        for (final ImportFilter importFilter : importFilters) {\n            final Script script = new Script();\n\n            script.descriptionProperty.setValue(importFilter.getDescription());\n            script.scriptProperty.setValue(importFilter.getScript());\n            script.importFilter = importFilter;\n            script.enabledProperty.setValue(true);\n\n            tableView.getItems().add(script);\n        }\n\n        loadScripts();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private  void handleOkayCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n\n        if (acceptanceConsumerProperty.get() != null) {\n            final List<ImportFilter> filterList = new ArrayList<>();\n\n            for (final Script script : tableView.getItems()) {\n                if (script.enabledProperty.get()) {\n                    filterList.add(script.importFilter);\n                }\n            }\n\n            acceptanceConsumerProperty.get().accept(filterList);\n        }\n    }\n\n    @FXML\n    private void handleUpAction() {\n        Collections.swap(tableView.getItems(), selectedIndexProperty.get(), selectedIndexProperty.get() - 1);\n    }\n\n    @FXML\n    private void handleDownAction() {\n        Collections.swap(tableView.getItems(), selectedIndexProperty.get(), selectedIndexProperty.get() + 1);\n    }\n\n    public void setAcceptanceConsumer(final Consumer<List<ImportFilter>> acceptanceConsumer) {\n        acceptanceConsumerProperty.setValue(acceptanceConsumer);\n    }\n\n    private static class Script {\n        final BooleanProperty enabledProperty = new SimpleBooleanProperty(false);\n\n        final StringProperty descriptionProperty = new SimpleStringProperty();\n\n        final StringProperty scriptProperty = new SimpleStringProperty();\n\n        ImportFilter importFilter;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/PackDatabaseDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog;\n\nimport java.io.File;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.PasswordField;\nimport javafx.scene.control.TextField;\nimport javafx.stage.FileChooser;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.uifx.tasks.PackDatabaseTask;\nimport jgnash.uifx.util.FileChooserFactory;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * Utility Dialog/Controller for changing the password of a relational database.\n *\n * @author Craig Cavanaugh\n */\npublic class PackDatabaseDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button okayButton;\n\n    @FXML\n    private TextField databaseTextField;\n\n    @FXML\n    private PasswordField passwordField;\n\n    @FXML\n    private ResourceBundle resources;\n\n\n    @FXML\n    void initialize() {\n        okayButton.disableProperty().bind(Bindings.isEmpty(databaseTextField.textProperty()));\n    }\n\n    @FXML\n    private void handleOkAction() {\n        ((Stage) parent.get().getWindow()).close();\n\n        PackDatabaseTask.start(databaseTextField.getText(), passwordField.getText().toCharArray());\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleDatabaseButtonAction() {\n        final FileChooser fileChooser = FileChooserFactory.getDataStoreChooser(DataStoreType.H2_DATABASE,\n                DataStoreType.H2MV_DATABASE, DataStoreType.HSQL_DATABASE);\n\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null && file.exists()) {\n            databaseTextField.setText(file.getAbsolutePath());\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/RemoteConnectionDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog;\n\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.PasswordField;\nimport javafx.scene.control.TextField;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.jpa.JpaNetworkServer;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Utility Dialog/Controller for collection remote database connection information.\n *\n * @author Craig Cavanaugh\n */\npublic class RemoteConnectionDialogController {\n\n    private static final String LAST_PORT = \"lastPort\";\n\n    private static final String LAST_HOST = \"lastHost\";\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private IntegerTextField portTextField;\n\n    @FXML\n    private Button okayButton;\n\n    @FXML\n    private TextField hostTextField;\n\n    @FXML\n    private PasswordField passwordField;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private boolean result = false;\n\n    @FXML\n    void initialize() {\n        okayButton.disableProperty().bind(Bindings.isEmpty(hostTextField.textProperty())\n                .or(Bindings.isEmpty(portTextField.textProperty())));\n\n        final Preferences preferences = Preferences.userNodeForPackage(RemoteConnectionDialogController.class);\n\n        hostTextField.setText(preferences.get(LAST_HOST, \"localhost\"));\n        portTextField.setInteger(preferences.getInt(LAST_PORT, JpaNetworkServer.DEFAULT_PORT));\n    }\n\n    @FXML\n    private void handleOkAction() {\n        result = true;\n\n        final Preferences preferences = Preferences.userNodeForPackage(RemoteConnectionDialogController.class);\n        preferences.put(LAST_HOST, hostTextField.getText());\n        preferences.putInt(LAST_PORT, portTextField.getInteger());\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    public char[] getPassword() {\n        return passwordField.getText().toCharArray();\n    }\n\n    public boolean getResult() {\n        return result;\n    }\n\n    public String getHost() {\n        return hostTextField.getText();\n    }\n\n    public int getPort() {\n        return portTextField.getInteger();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/TagManagerDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ColorPicker;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\nimport javafx.util.Callback;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Tag;\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.TextFieldEx;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.EncodeDecode;\n\nimport static jgnash.uifx.resource.font.MaterialDesignLabel.MDIcon;\nimport static jgnash.uifx.views.register.TransactionTagPane.ICON_SCALE;\n\n/**\n * Tag Manager dialog controller\n *\n * @author Craig Cavanaugh\n */\npublic class TagManagerDialogController implements MessageListener {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ColorPicker colorPicker;\n\n    @FXML\n    private Button saveButton;\n\n    @FXML\n    private TextArea descriptionTextArea;\n\n    @FXML\n    private TextFieldEx nameField;\n\n    @FXML\n    private ComboBox<MDIcon> iconCombo;\n\n    @FXML\n    private Button duplicateButton;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private ListView<Tag> tagListView;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final Map<Tag, Boolean> lockedMap = new HashMap<>();\n    private final BooleanProperty tagLocked = new SimpleBooleanProperty();\n\n    @FXML\n    private void initialize() {\n        deleteButton.disableProperty().bind(tagListView.getSelectionModel().selectedItemProperty().isNull().or(tagLocked));\n        duplicateButton.disableProperty().bind(tagListView.getSelectionModel().selectedItemProperty().isNull());\n        nameField.disableProperty().bind(tagListView.getSelectionModel().selectedItemProperty().isNull());\n        saveButton.disableProperty().bind(tagListView.getSelectionModel().selectedItemProperty().isNull());\n        tagListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);\n        iconCombo.setEditable(false);\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.TAG);\n\n        JavaFXUtils.runLater(this::loadTagListView);\n\n        JavaFXUtils.runLater(() -> tagListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                JavaFXUtils.runLater(() -> loadForm(newValue));\n            }\n        }));\n\n        iconCombo.setCellFactory(new ListViewListCellCallback());\n        iconCombo.setButtonCell(new FAIconListCell());\n\n        // load the icons while filtering for only icons intended for use as Tags\n        JavaFXUtils.runLater(() -> iconCombo.getItems().addAll(Arrays.stream(MDIcon.values())\n                .filter(MDIcon::isTag).collect(Collectors.toList())));\n\n        tagListView.setCellFactory(new TagListViewListCellCallback());\n\n        // reset to default values\n        handleResetAction();\n    }\n\n    private void loadForm(final Tag tag) {\n        nameField.setText(tag.getName());\n        descriptionTextArea.setText(tag.getDescription());\n        colorPicker.setValue(Color.valueOf(EncodeDecode.longToColorString(tag.getColor())));\n\n        // updates delete lock\n        tagLocked.setValue(lockedMap.getOrDefault(tag, Boolean.FALSE));\n\n        new Thread(() -> {\n            final int unicode = tag.getShape();\n            for (final MDIcon faIcon : MDIcon.values()) {\n                if (unicode == faIcon.getUnicode()) {\n                    JavaFXUtils.runLater(() -> iconCombo.setValue(faIcon));\n                }\n            }\n        }).start();\n    }\n\n    @FXML\n    private void handleNewAction() {\n        final Tag tag = new Tag();\n        tag.setName(resources.getString(\"Word.NewTag\"));\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n        engine.addTag(tag);\n    }\n\n    @FXML\n    private void handleDuplicateAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final Tag tag : tagListView.getSelectionModel().getSelectedItems()) {\n            try {\n                final Tag newTag = (Tag) tag.clone();\n                if (!engine.addTag(newTag)) {\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.TagDuplicate\"));\n                }\n            } catch (final CloneNotSupportedException e) {\n                Logger.getLogger(TagManagerDialogController.class.getName()).log(Level.SEVERE, e.toString(), e);\n            }\n        }\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        engine.removeTag(tagListView.getSelectionModel().getSelectedItem());\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        MessageBus.getInstance().unregisterListener(this, MessageChannel.TAG);\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        if (message.getEvent() == ChannelEvent.TAG_ADD || message.getEvent() == ChannelEvent.TAG_REMOVE\n                    || message.getEvent() == ChannelEvent.TAG_MODIFY) {\n            JavaFXUtils.runLater(this::loadTagListView);\n        }\n    }\n\n    private void loadTagListView() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        lockedMap.clear();\n\n        final List<Tag> tagList = new ArrayList<>(engine.getTags());\n        Collections.sort(tagList);\n\n        tagListView.getItems().setAll(tagList);\n\n        final Set<Tag> usedTags = engine.getTagsInUse();\n        for (final Tag tag : tagList) {\n            lockedMap.put(tag, usedTags.contains(tag));\n        }\n    }\n\n    public static void showTagManager() {\n        final FXMLUtils.Pair<TagManagerDialogController> pair =\n                FXMLUtils.load(TagManagerDialogController.class.getResource(\"TagManagerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.TagManager\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleSaveAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Tag tag = tagListView.getSelectionModel().getSelectedItem();\n\n        if (tag != null) {\n            tag.setName(nameField.getText());\n            tag.setDescription(descriptionTextArea.getText());\n            tag.setShape(iconCombo.getValue().getUnicode());\n            tag.setColor(EncodeDecode.colorStringToLong(colorPicker.getValue().toString()));\n\n            engine.updateTag(tag);\n        }\n    }\n\n    @FXML\n    private void handleResetAction() {\n        JavaFXUtils.runLater(() -> {\n            nameField.setText(\"\");\n            descriptionTextArea.setText(\"\");\n            colorPicker.setValue(Color.BLACK);\n            iconCombo.setValue(MDIcon.CIRCLE);\n        });\n    }\n\n    private static class ListViewListCellCallback implements Callback<ListView<MDIcon>, ListCell<MDIcon>> {\n        @Override\n        public ListCell<MaterialDesignLabel.MDIcon> call(ListView<MDIcon> param) {\n            return new FAIconListCell();\n        }\n    }\n\n    private static class FAIconListCell extends ListCell<MDIcon> {\n        private final Label label = new Label();\n\n        {\n            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n        }\n\n        @Override\n        protected void updateItem(final MDIcon item, final boolean empty) {\n            super.updateItem(item, empty);\n\n            if (item == null || empty) {\n                setGraphic(null);\n            } else {\n                label.setGraphic(new MaterialDesignLabel(item, MaterialDesignLabel.DEFAULT_SIZE));\n                setGraphic(label);\n            }\n        }\n    }\n\n    private static class TagListViewListCellCallback implements Callback<ListView<Tag>, ListCell<Tag>> {\n        @Override\n        public ListCell<Tag> call(ListView<Tag> param) {\n            return new TagListCell();\n        }\n\n        private static class TagListCell extends ListCell<Tag> {\n            private final Label label = new Label();\n\n            @Override\n            protected void updateItem(final Tag item, final boolean empty) {\n                super.updateItem(item, empty);  // required\n\n                if (item == null || empty) {\n                    setGraphic(null);\n                    setText(\"\");\n                } else {\n                    label.setGraphic(MaterialDesignLabel.fromInteger(item.getShape(),\n                            MaterialDesignLabel.DEFAULT_SIZE * ICON_SCALE, item.getColor()));\n                    label.setText(item.toString());\n                    setGraphic(label);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/currency/AddRemoveCurrencyController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.currency;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.Set;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TextField;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DefaultCurrencies;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.control.LockedCommodityListCell;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.util.LockedCommodityNode;\n\n/**\n * Add / remove currency controller.\n *\n * @author Craig Cavanaugh\n */\npublic class AddRemoveCurrencyController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button addButton;\n\n    @FXML\n    private TextField newCurrencyTextField;\n\n    @FXML\n    private ListView<CurrencyNode> availableList;\n\n    @FXML\n    private ListView<LockedCommodityNode<CurrencyNode>> selectedList;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final BooleanProperty validProperty = new SimpleBooleanProperty(true);\n\n    @FXML   \n    void initialize() {\n        availableList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n        selectedList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        selectedList.setCellFactory(param -> new LockedCommodityListCell<>());\n\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        newCurrencyTextField.textProperty().addListener((observable, oldValue, newValue)\n                -> validProperty.set(engine.getCurrency(newCurrencyTextField.getText()) == null));\n\n        addButton.disableProperty().bind(newCurrencyTextField.textProperty().isEmpty().or(validProperty.not()));\n\n        loadModel();\n    }\n\n    private void loadModel() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        // Defaults\n        final Set<CurrencyNode> defaultNodes = DefaultCurrencies.generateCurrencies();\n\n        // Active\n        final Set<CurrencyNode> activeNodes = engine.getActiveCurrencies();\n\n        // Available\n        final List<CurrencyNode> availNodes = engine.getCurrencies();\n\n        // remove any overlap between the available and the default\n        availNodes.forEach(defaultNodes::remove);\n\n        availableList.getItems().addAll(defaultNodes);\n\n        final ArrayList<LockedCommodityNode<CurrencyNode>> list = new ArrayList<>();\n\n        for (final CurrencyNode node : availNodes) {\n            if (activeNodes.contains(node)) {\n                list.add(new LockedCommodityNode<>(node, true));\n            } else {\n                list.add(new LockedCommodityNode<>(node, false));\n            }\n        }\n\n        selectedList.getItems().addAll(list);\n\n        FXCollections.sort(availableList.getItems());\n        FXCollections.sort(selectedList.getItems());\n    }\n\n    @FXML\n    private void handleAddAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final CurrencyNode currencyNode : availableList.getSelectionModel().getSelectedItems()) {\n            engine.addCurrency(currencyNode);\n\n            availableList.getItems().remove(currencyNode);\n            selectedList.getItems().add(new LockedCommodityNode<>(currencyNode, false));\n        }\n\n        FXCollections.sort(selectedList.getItems());\n    }\n\n    @FXML\n    private void handleRemoveAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n\n        selectedList.getSelectionModel().getSelectedItems().stream()\n                .filter(node -> node != null && !node.isLocked()).forEach(node -> {\n            selectedList.getItems().remove(node);\n            availableList.getItems().add(node.getNode());\n\n            engine.removeCommodity(node.getNode());\n        });\n\n        FXCollections.sort(availableList.getItems());\n    }\n\n\n    @FXML\n    private void handleNewCurrencyAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final CurrencyNode node = DefaultCurrencies.buildCustomNode(newCurrencyTextField.getText());\n\n        // the add could fail if the commodity symbol is a duplicate\n        if (engine.addCurrency(node)) {\n            selectedList.getItems().add(new LockedCommodityNode<>(node, false));\n            FXCollections.sort(selectedList.getItems());\n            newCurrencyTextField.clear();\n        }\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/currency/EditExchangeRatesController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.currency;\n\nimport java.math.BigDecimal;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.ExecutionException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.concurrent.Task;\nimport javafx.concurrent.WorkerStateEvent;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ProgressBar;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ExchangeRate;\nimport jgnash.engine.ExchangeRateHistoryNode;\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.net.currency.CurrencyUpdateFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.control.BigDecimalTableCell;\nimport jgnash.uifx.control.CurrencyComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.ShortDateTableCell;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for editing currency exchange rates.\n *\n * @author Craig Cavanaugh\n */\npublic class EditExchangeRatesController implements MessageListener {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private DatePickerEx datePicker;\n\n    @FXML\n    private CurrencyComboBox baseCurrencyComboBox;\n\n    @FXML\n    private CurrencyComboBox targetCurrencyComboBox;\n\n    @FXML\n    private DecimalTextField exchangeRateField;\n\n    @FXML\n    private Button addButton;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private Button clearButton;\n\n    @FXML\n    private TableView<ExchangeRateHistoryNode> exchangeRateTable;\n\n    @FXML\n    private Button updateOnlineButton;\n\n    @FXML\n    private ProgressBar progressBar;\n\n    @FXML\n    private Button stopButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final SimpleObjectProperty<ExchangeRateHistoryNode> selectedHistoryNode = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<ExchangeRate> selectedExchangeRate = new SimpleObjectProperty<>();\n\n    private Task<Void> updateTask = null;\n\n    private volatile boolean requestCancel = false;\n\n    public static void show() {\n        final FXMLUtils.Pair<EditExchangeRatesController> pair =\n                FXMLUtils.load(EditExchangeRatesController.class.getResource(\"EditExchangeRates.fxml\"),\n                        ResourceUtils.getString(\"Title.EditExchangeRates\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    void initialize() {\n        exchangeRateField.scaleProperty().set(MathConstants.EXCHANGE_RATE_ACCURACY);\n\n        exchangeRateTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        exchangeRateTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        final TableColumn<ExchangeRateHistoryNode, LocalDate> dateColumn = new TableColumn<>(resources.getString(\"Column.Date\"));\n        dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getLocalDate()));\n        dateColumn.setCellFactory(cell -> new ShortDateTableCell<>());\n        exchangeRateTable.getColumns().add(dateColumn);\n\n        final NumberFormat decimalFormat = NumberFormat.getInstance();\n        if (decimalFormat instanceof DecimalFormat) {\n            decimalFormat.setMinimumFractionDigits(MathConstants.EXCHANGE_RATE_ACCURACY);\n            decimalFormat.setMaximumFractionDigits(MathConstants.EXCHANGE_RATE_ACCURACY);\n        }\n\n        final TableColumn<ExchangeRateHistoryNode, BigDecimal> rateColumn\n                = new TableColumn<>(resources.getString(\"Column.ExchangeRate\"));\n        rateColumn.setCellValueFactory(param -> {\n            if (param == null || selectedExchangeRate.get() == null) {\n                return null;\n            }\n\n            if (selectedExchangeRate.get().getRateId().startsWith(baseCurrencyComboBox.getValue().getSymbol())) {\n                return new SimpleObjectProperty<>(param.getValue().getRate());\n            }\n\n            return new SimpleObjectProperty<>(BigDecimal.ONE.divide(param.getValue().getRate(),\n                    MathConstants.mathContext));\n        });\n        rateColumn.setCellFactory(cell -> new BigDecimalTableCell<>(decimalFormat));\n        exchangeRateTable.getColumns().add(rateColumn);\n\n        baseCurrencyComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue)\n                -> handleExchangeRateSelectionChange());\n\n        targetCurrencyComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue)\n                -> handleExchangeRateSelectionChange());\n\n        selectedHistoryNode.bind(exchangeRateTable.getSelectionModel().selectedItemProperty());\n\n        deleteButton.disableProperty().bind(selectedHistoryNode.isNull());\n\n        clearButton.disableProperty().bind(exchangeRateField.textProperty().isEmpty());\n\n        addButton.disableProperty().bind(exchangeRateField.textProperty().isEmpty()\n                .or(Bindings.equal(baseCurrencyComboBox.getSelectionModel().selectedItemProperty(),\n                        targetCurrencyComboBox.getSelectionModel().selectedItemProperty())));\n\n        selectedExchangeRate.addListener((observable, oldValue, newValue)\n                -> JavaFXUtils.runLater(EditExchangeRatesController.this::loadExchangeRateHistory));\n\n        selectedHistoryNode.addListener((observable, oldValue, newValue)\n                -> JavaFXUtils.runLater(EditExchangeRatesController.this::updateForm));\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.COMMODITY);\n\n        // Install a listener to unregister from the message bus when the window closes\n        parent.addListener((observable, oldValue, scene) -> {\n            if (scene != null) {\n                scene.windowProperty().addListener((observable1, oldValue1, window)\n                        -> window.addEventHandler(WindowEvent.WINDOW_HIDING, event -> {\n                    handleStopAction();\n                    Logger.getLogger(EditExchangeRatesController.class.getName()).info(\"Unregistered from the message bus\");\n                    MessageBus.getInstance().unregisterListener(EditExchangeRatesController.this, MessageChannel.COMMODITY);\n                }));\n            }\n        });\n    }\n\n    private void updateForm() {\n        if (selectedHistoryNode.get() != null) {\n            datePicker.setValue(selectedHistoryNode.get().getLocalDate());\n            exchangeRateField.setDecimal(selectedHistoryNode.get().getRate());\n        } else {\n            handleClearAction();\n        }\n    }\n\n    private void loadExchangeRateHistory() {\n        if (selectedExchangeRate.get() == null) {\n            exchangeRateTable.getItems().clear();\n        } else {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            exchangeRateTable.getItems().setAll(selectedExchangeRate.get().getHistory());\n            FXCollections.sort(exchangeRateTable.getItems());\n        }\n    }\n\n    private void handleExchangeRateSelectionChange() {\n        if (baseCurrencyComboBox.getValue() != null && targetCurrencyComboBox.getValue() != null) {\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            selectedExchangeRate.set(engine.getExchangeRate(baseCurrencyComboBox.getValue(),\n                    targetCurrencyComboBox.getValue()));\n        }\n    }\n\n    @FXML\n    private void handleAddAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        engine.setExchangeRate(baseCurrencyComboBox.getValue(), targetCurrencyComboBox.getValue(),\n                exchangeRateField.getDecimal(), datePicker.getValue());\n\n        handleClearAction();\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        // Create a defensive list of the selected history nodes\n        final List<ExchangeRateHistoryNode> historyNodes\n                = new ArrayList<>(exchangeRateTable.getSelectionModel().getSelectedItems());\n\n        for (final ExchangeRateHistoryNode historyNode : historyNodes) {\n            engine.removeExchangeRateHistory(selectedExchangeRate.get(), historyNode);\n        }\n    }\n\n    @FXML\n    private void handleClearAction() {\n        datePicker.setValue(LocalDate.now());\n        exchangeRateField.clear();\n    }\n\n    @FXML\n    private void handleUpdateAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        updateOnlineButton.disableProperty().set(true);\n\n        updateTask = new Task<>() {\n            @Override\n            protected Void call() {\n\n                final List<CurrencyNode> list = engine.getCurrencies();\n\n                // need to track the total processed count\n                long processedHistory = 0;\n\n                for (final CurrencyNode source : list) {\n                    for (final CurrencyNode target : list) {\n                        if (!source.equals(target) && source.getSymbol().compareToIgnoreCase(target.getSymbol()) > 0\n                                && !requestCancel) {\n\n                            final Optional<BigDecimal> rate = CurrencyUpdateFactory.getExchangeRate(source, target);\n\n                            if (rate.isPresent()) {\n                                engine.setExchangeRate(source, target, rate.get());\n\n                                updateProgress(++processedHistory, list.size() - 1);\n                            }\n                        }\n                    }\n                }\n\n                return null;\n            }\n        };\n\n        updateTask.addEventHandler(WorkerStateEvent.WORKER_STATE_SUCCEEDED, event -> taskComplete());\n        updateTask.addEventHandler(WorkerStateEvent.WORKER_STATE_CANCELLED, event -> taskComplete());\n        updateTask.addEventHandler(WorkerStateEvent.WORKER_STATE_FAILED, event -> taskComplete());\n\n        progressBar.progressProperty().bind(updateTask.progressProperty());\n\n        final Thread thread = new Thread(updateTask);\n        thread.setDaemon(true);\n        thread.start();\n    }\n\n    private void taskComplete() {\n        requestCancel = false;\n\n        progressBar.progressProperty().unbind();\n        progressBar.progressProperty().set(0);\n\n        updateOnlineButton.disableProperty().set(false);\n\n        updateTask = null;\n    }\n\n    @FXML\n    private void handleStopAction() {\n        if (updateTask != null) {\n            requestCancel = true;\n\n            try {\n                updateTask.get();    // wait for a graceful exit\n            } catch (final ExecutionException | InterruptedException e) {\n                Logger.getLogger(EditExchangeRatesController.class.getName()).log(Level.SEVERE,\n                        e.getLocalizedMessage(), e);\n            }\n        }\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        handleStopAction();\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        switch (message.getEvent()) {\n            case EXCHANGE_RATE_ADD:\n            case EXCHANGE_RATE_REMOVE:\n                final ExchangeRate rate = message.getObject(MessageProperty.EXCHANGE_RATE);\n                if (rate.equals(selectedExchangeRate.get())) {\n                    JavaFXUtils.runLater(this::loadExchangeRateHistory);\n                }\n                break;\n            default:\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/currency/ModifyCurrencyController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.currency;\n\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.TextField;\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller of modifying currencies.\n *\n * @author Craig Cavanaugh\n */\npublic class ModifyCurrencyController implements MessageListener {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button applyButton;\n\n    @FXML\n    private ListView<CurrencyNode> listView;\n\n    @FXML\n    private TextField symbolTextField;\n\n    @FXML\n    private TextField descriptionTextField;\n\n    @FXML\n    private IntegerTextField scaleTextField;\n\n    @FXML\n    private TextField prefixTextField;\n\n    @FXML\n    private TextField suffixTextField;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final SimpleObjectProperty<CurrencyNode> selectedCurrency = new SimpleObjectProperty<>();\n\n    @FXML\n    void initialize() {\n        MessageBus.getInstance().registerListener(this, MessageChannel.COMMODITY);\n\n        selectedCurrency.bind(listView.getSelectionModel().selectedItemProperty());\n\n        selectedCurrency.addListener((observable, oldValue, newValue) -> JavaFXUtils.runLater(this::loadForm));\n\n        // unregister when the window closes\n        parent.addListener((observable, oldValue, newValue)\n                -> newValue.windowProperty().addListener((observable1, oldValue1, newValue1) ->\n                newValue1.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST,\n                        event -> MessageBus.getInstance().unregisterListener(ModifyCurrencyController.this,\n                                MessageChannel.COMMODITY))));\n\n        applyButton.disableProperty().bind(Bindings.or(selectedCurrency.isNull(),\n                scaleTextField.textProperty().isEmpty()));\n\n        symbolTextField.disableProperty().bind(selectedCurrency.isNotNull());\n\n        loadModel();\n    }\n\n    private void loadModel() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        listView.getItems().setAll(engine.getCurrencies());\n\n        FXCollections.sort(listView.getItems());\n    }\n\n    private void loadForm() {\n        if (selectedCurrency.get() != null) {\n            symbolTextField.setText(selectedCurrency.get().getSymbol());\n            descriptionTextField.setText(selectedCurrency.get().getDescription());\n            scaleTextField.setInteger((int) selectedCurrency.get().getScale());\n            prefixTextField.setText(selectedCurrency.get().getPrefix());\n            suffixTextField.setText(selectedCurrency.get().getSuffix());\n        } else {\n            clearForm();\n        }\n    }\n\n    private void clearForm() {\n        symbolTextField.clear();\n        descriptionTextField.clear();\n        scaleTextField.clear();\n        prefixTextField.clear();\n        suffixTextField.clear();\n\n        listView.getSelectionModel().clearSelection();\n    }\n\n    @FXML\n    private void handleClearAction() {\n        clearForm();\n    }\n\n    @FXML\n    private void handleApplyAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final CurrencyNode newNode = new CurrencyNode();\n\n        newNode.setSymbol(symbolTextField.getText());\n        newNode.setDescription(descriptionTextField.getText());\n        newNode.setPrefix(prefixTextField.getText());\n        newNode.setScale(scaleTextField.getInteger().byteValue());\n        newNode.setSuffix(suffixTextField.getText());\n\n        if (!engine.updateCommodity(selectedCurrency.get(), newNode)) {\n            StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.CurrencyUpdate\", newNode.getSymbol()));\n        }\n\n        loadModel();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        final CommodityNode node = message.getObject(MessageProperty.COMMODITY);\n\n        if (node instanceof CurrencyNode) {\n            switch (message.getEvent()) {\n                case CURRENCY_REMOVE:\n                    listView.getItems().remove(node);\n                    if (node.equals(selectedCurrency.get())) {\n                        handleClearAction();\n                    }\n                    break;\n                case CURRENCY_REMOVE_FAILED:\n                    StaticUIMethods.displayError(resources.getString(\"Message.Warn.CurrencyInUse\"));\n                    break;\n                case CURRENCY_ADD:\n                case CONFIG_MODIFY:\n                    handleClearAction();\n                    loadModel();\n                    break;\n                case CURRENCY_ADD_FAILED:\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.AddCurrency\"));\n                    break;\n                case CURRENCY_MODIFY_FAILED:\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.ModifyCurrency\"));\n                    break;\n                default:\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/AccountTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.TextField;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.uifx.views.AccountBalanceDisplayMode;\n\n/**\n * Controller for Account options.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountTabController {\n\n    @FXML\n    private RadioButton accountOnlyRadioButton;\n\n    @FXML\n    private RadioButton allRadioButton;\n\n    @FXML\n    private RadioButton noAccountsRadioButton;\n\n    @FXML\n    private RadioButton creditAccountsRadioButton;\n\n    @FXML\n    private RadioButton incomeExpenseAccountsRadioButton;\n\n    @FXML\n    private CheckBox useAccountingTermsCheckBox;\n\n    @FXML\n    private TextField accountSeparatorTextField;\n\n    @FXML\n    private void initialize() {\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        if (engine != null) {\n            accountSeparatorTextField.setText(engine.getAccountSeparator());\n\n            accountSeparatorTextField.textProperty().addListener((observable, oldValue, newValue) -> {\n                if (newValue != null && newValue.length() > 0) {\n                    engine.setAccountSeparator(newValue);\n                }\n            });\n        } else {\n            accountSeparatorTextField.setDisable(true);\n        }\n\n        useAccountingTermsCheckBox.selectedProperty().bindBidirectional(Options.useAccountingTermsProperty());\n\n        switch (AccountBalanceDisplayManager.accountBalanceDisplayMode().get()) {\n            case NONE:\n                noAccountsRadioButton.setSelected(true);\n                break;\n            case REVERSE_CREDIT:\n                creditAccountsRadioButton.setSelected(true);\n                break;\n            case REVERSE_INCOME_EXPENSE:\n                incomeExpenseAccountsRadioButton.setSelected(true);\n                break;\n        }\n\n        if (Options.globalBayesProperty().get()) {\n            allRadioButton.setSelected(true);\n        } else {\n            accountOnlyRadioButton.setSelected(true);\n        }\n\n        noAccountsRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue) {\n                AccountBalanceDisplayManager.setDisplayMode(AccountBalanceDisplayMode.NONE);\n            }\n        });\n\n        creditAccountsRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue) {\n                AccountBalanceDisplayManager.setDisplayMode(AccountBalanceDisplayMode.REVERSE_CREDIT);\n            }\n        });\n\n        incomeExpenseAccountsRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue) {\n                AccountBalanceDisplayManager.setDisplayMode(AccountBalanceDisplayMode.REVERSE_INCOME_EXPENSE);\n            }\n        });\n\n        allRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue) {\n                Options.globalBayesProperty().set(true);\n            }\n        });\n\n        accountOnlyRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue) {\n                Options.globalBayesProperty().set(false);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/DataProviderTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.uifx.dialog.options;\n\nimport java.awt.Desktop;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.control.TextFieldEx;\nimport jgnash.util.LogUtil;\n\nimport static jgnash.net.security.iex.IEXParser.IEX_SECRET_KEY;\n\n/**\n * Controller for configuring Data Providers\n *\n * @author Craig Cavanaugh\n */\npublic class DataProviderTabController {\n\n    @FXML\n    private TextFieldEx iexPrivateKeyTextField;\n\n    @FXML\n    private void initialize() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            final String key = engine.getPreference(IEX_SECRET_KEY);\n\n            if (key != null && key.length() > 0) {\n                iexPrivateKeyTextField.setText(key);\n            }\n\n            // save on loss of focus\n            iexPrivateKeyTextField.focusedProperty().addListener((observable, oldValue, newValue) -> {\n                if (!newValue) {\n                    handleIexCloudKey();\n                }\n            });\n        } else {\n            iexPrivateKeyTextField.setDisable(true);\n        }\n    }\n\n    @FXML\n    private void handleIexCloudKey() {\n        final String key = iexPrivateKeyTextField.getText();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        engine.setPreference(IEX_SECRET_KEY, key);\n    }\n\n    @FXML\n    private void handleHyperLink() {\n        if (Desktop.isDesktopSupported()) {\n            new Thread(() -> {\n                try {\n                    Desktop.getDesktop().browse(new URI(\"https://iexcloud.io\"));\n                } catch (IOException | URISyntaxException ioe) {\n                    LogUtil.logSevere(DataProviderTabController.class, ioe);\n                }\n            }).start();\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/FormatsTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ComboBox;\n\n/**\n * Controls formats used to display information\n *\n * @author Craig Cavanaugh\n */\npublic class FormatsTabController {\n\n    @FXML\n    private ComboBox<String> fullNumberFormatComboBox;\n\n    @FXML\n    private ComboBox<String> shortNumberFormatComboBox;\n\n    @FXML\n    private ComboBox<String> dateFormatComboBox;\n\n    @FXML\n    private void initialize() {\n        dateFormatComboBox.getItems().setAll(DateUtils.getAvailableShortDateFormats());\n        dateFormatComboBox.setValue(Options.shortDateFormatProperty().getValue());\n\n        fullNumberFormatComboBox.getItems().setAll(NumericFormats.getKnownFullPatterns());\n        fullNumberFormatComboBox.setValue(Options.fullNumericFormatProperty().getValue());\n\n        shortNumberFormatComboBox.getItems().setAll(NumericFormats.getKnownShortPatterns());\n        shortNumberFormatComboBox.setValue(Options.shortNumericFormatProperty().getValue());\n\n        dateFormatComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null && !newValue.isBlank()) {\n                Options.shortDateFormatProperty().setValue(newValue);\n            }\n        });\n\n        fullNumberFormatComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null && !newValue.isBlank()) {\n                Options.fullNumericFormatProperty().setValue(newValue);\n            }\n        });\n\n        shortNumberFormatComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null && !newValue.isBlank()) {\n                Options.shortNumericFormatProperty().setValue(newValue);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/GeneralTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.ToggleGroup;\n\nimport jgnash.uifx.Options;\n\n/**\n * Controller for General Options.\n *\n * @author Craig Cavanaugh\n */\npublic class GeneralTabController {\n\n    @FXML\n    private CheckBox filterRegexEnabledCheckBox;\n\n    @FXML\n    private RadioButton windowsStyleRadioButton;\n\n    @FXML\n    private RadioButton macOSStyleRadioButton;\n\n    @FXML\n    private RadioButton linuxStyleRadioButton;\n\n    @FXML\n    private CheckBox animationsEnabledCheckBox;\n\n    @FXML\n    private CheckBox selectOnFocusCheckBox;\n\n    @FXML\n    private CheckBox autoPackRegisterTableCheckBox;\n\n    @FXML\n    private ToggleGroup toggleGroup;\n\n    @FXML\n    private void initialize() {\n        selectOnFocusCheckBox.selectedProperty().bindBidirectional(Options.selectOnFocusProperty());\n        animationsEnabledCheckBox.selectedProperty().bindBidirectional(Options.animationsEnabledProperty());\n        filterRegexEnabledCheckBox.selectedProperty().bindBidirectional(Options.regexForFiltersProperty());\n\n        autoPackRegisterTableCheckBox.selectedProperty().bindBidirectional(Options.autoPackTablesProperty());\n\n        switch (Options.buttonOrderProperty().get()) {\n            case ButtonBar.BUTTON_ORDER_LINUX:\n                linuxStyleRadioButton.setSelected(true);\n                break;\n            case ButtonBar.BUTTON_ORDER_WINDOWS:\n            default:\n                windowsStyleRadioButton.setSelected(true);\n                break;\n            case ButtonBar.BUTTON_ORDER_MAC_OS:\n                macOSStyleRadioButton.setSelected(true);\n                break;\n        }\n\n        toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue == linuxStyleRadioButton) {\n                Options.buttonOrderProperty().set(ButtonBar.BUTTON_ORDER_LINUX);\n            } else if (newValue == macOSStyleRadioButton) {\n                Options.buttonOrderProperty().set(ButtonBar.BUTTON_ORDER_MAC_OS);\n            } else {\n                Options.buttonOrderProperty().set(ButtonBar.BUTTON_ORDER_WINDOWS);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/NetworkTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.PasswordField;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory;\nimport javafx.scene.control.TextField;\n\nimport jgnash.net.ConnectionFactory;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.net.NetworkAuthenticator;\n\n/**\n * Controller for Network Options.\n *\n * @author Craig Cavanaugh\n */\npublic class NetworkTabController {\n\n    @FXML\n    private CheckBox useProxyCheckBox;\n\n    @FXML\n    private TextField hostTextField;\n\n    @FXML\n    private IntegerTextField portTextField;\n\n    @FXML\n    private CheckBox requireAuthCheckBox;\n\n    @FXML\n    private TextField userNameTextField;\n\n    @FXML\n    private PasswordField passwordField;\n\n    @FXML\n    private Spinner<Integer> timeoutSpinner;\n\n    @FXML\n    private void initialize() {\n\n        timeoutSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(\n                ConnectionFactory.MIN_TIMEOUT, ConnectionFactory.MAX_TIMEOUT,\n                ConnectionFactory.getConnectionTimeout(), 1));\n\n        hostTextField.disableProperty().bind(useProxyCheckBox.selectedProperty().not());\n        portTextField.disableProperty().bind(useProxyCheckBox.selectedProperty().not());\n\n        userNameTextField.disableProperty().bind(requireAuthCheckBox.selectedProperty().not());\n        passwordField.disableProperty().bind(requireAuthCheckBox.selectedProperty().not());\n\n        useProxyCheckBox.setSelected(NetworkAuthenticator.isProxyUsed());\n        hostTextField.setText(NetworkAuthenticator.getHost());\n        portTextField.setInteger(NetworkAuthenticator.getPort());\n\n        requireAuthCheckBox.setSelected(NetworkAuthenticator.isAuthenticationUsed());\n        userNameTextField.setText(NetworkAuthenticator.getName());\n        passwordField.setText(NetworkAuthenticator.getPassword());\n\n        useProxyCheckBox.selectedProperty().addListener((observable, oldValue, newValue)\n                -> NetworkAuthenticator.setUseProxy(newValue));\n\n        hostTextField.textProperty().addListener((observable, oldValue, newValue)\n                -> NetworkAuthenticator.setHost(newValue));\n\n        portTextField.textProperty().addListener((observable, oldValue, newValue)\n                -> NetworkAuthenticator.setPort(portTextField.getInteger()));\n\n        requireAuthCheckBox.selectedProperty().addListener((observable, oldValue, newValue)\n                -> NetworkAuthenticator.setUseAuthentication(newValue));\n\n        userNameTextField.textProperty().addListener((observable, oldValue, newValue)\n                -> NetworkAuthenticator.setName(newValue));\n\n        passwordField.textProperty().addListener((observable, oldValue, newValue)\n                -> NetworkAuthenticator.setPassword(newValue));\n\n        timeoutSpinner.getValueFactory().valueProperty().addListener((observable, oldValue, newValue)\n                -> ConnectionFactory.setConnectionTimeout(newValue));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/OptionDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.stage.Stage;\n\nimport jgnash.plugin.FxPlugin;\nimport jgnash.plugin.PluginFactory;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for application options.\n *\n * @author Craig Cavanaugh\n */\npublic class OptionDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    private static final String INDEX = \"index\";\n\n    @FXML\n    private TabPane tabPane;\n\n    @FXML\n    void initialize() {\n\n        // Load the plugin tabs into the dialog\n        JavaFXUtils.runLater(() -> PluginFactory.getPlugins().stream().filter(plugin -> plugin instanceof FxPlugin)\n                .forEachOrdered(plugin -> {\n                    final Node tab = ((FxPlugin) plugin).getOptionsNode();\n                    if (tab != null) {\n                        tabPane.getTabs().add(new Tab(plugin.getName(), tab));\n                    }\n                }));\n\n\n        JavaFXUtils.runLater(() -> {\n            final Preferences preferences = Preferences.userNodeForPackage(OptionDialogController.class);\n\n            tabPane.getSelectionModel().select(preferences.getInt(INDEX, 0));\n\n            tabPane.getSelectionModel()\n                    .selectedIndexProperty().addListener(new WeakChangeListener<>((observable, oldValue, newValue) -> {\n                if (newValue != null) {\n                    preferences.putInt(INDEX, newValue.intValue());\n                }\n            }));\n        });\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/RegisterTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.RadioButton;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.uifx.Options;\n\n/**\n * Controller for Register Options.\n *\n * @author Craig Cavanaguh\n */\npublic class RegisterTabController {\n\n    @FXML\n    private CheckBox restoreLastRegisterTab;\n\n    @FXML\n    private CheckBox rememberLastTranDateCheckBox;\n\n    @FXML\n    private CheckBox confirmDeleteCheckBox;\n\n    @FXML\n    private RadioButton disableReconcileCheckBox;\n\n    @FXML\n    private RadioButton reconcileBothCheckBox;\n\n    @FXML\n    private RadioButton reconcileIncomeExpenseCheckBox;\n\n    @FXML\n    private CheckBox enableAutoCompleteCheckBox;\n\n    @FXML\n    private CheckBox caseSensitiveCheckBox;\n\n    @FXML\n    private CheckBox fuzzyMatchCheckBox;\n\n    @FXML\n    private void initialize() {\n\n        confirmDeleteCheckBox.selectedProperty().bindBidirectional(Options.confirmOnTransactionDeleteProperty());\n        rememberLastTranDateCheckBox.selectedProperty().bindBidirectional(Options.rememberLastDateProperty());\n\n        fuzzyMatchCheckBox.disableProperty().bind(enableAutoCompleteCheckBox.selectedProperty().not());\n        caseSensitiveCheckBox.disableProperty().bind(enableAutoCompleteCheckBox.selectedProperty().not());\n\n        enableAutoCompleteCheckBox.selectedProperty().bindBidirectional(Options.useAutoCompleteProperty());\n        fuzzyMatchCheckBox.selectedProperty().bindBidirectional(Options.useFuzzyMatchForAutoCompleteProperty());\n        caseSensitiveCheckBox.selectedProperty().bindBidirectional(Options.autoCompleteIsCaseSensitiveProperty());\n\n        restoreLastRegisterTab.selectedProperty().bindBidirectional(Options.restoreLastTabProperty());\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        if (engine != null) {\n            if (ReconcileManager.isAutoReconcileDisabled()) {\n                disableReconcileCheckBox.setSelected(true);\n            } else if (ReconcileManager.getAutoReconcileBothSides()) {\n                reconcileBothCheckBox.setSelected(true);\n            } else {\n                reconcileIncomeExpenseCheckBox.setSelected(true);\n            }\n\n            disableReconcileCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {\n                if (newValue) {\n                    ReconcileManager.setDoNotAutoReconcile();\n                }\n            });\n\n            reconcileBothCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {\n                if (newValue) {\n                    ReconcileManager.setAutoReconcileBothSides(true);\n                }\n            });\n\n            reconcileIncomeExpenseCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> {\n                if (newValue) {\n                    ReconcileManager.setAutoReconcileIncomeExpense(true);\n                }\n            });\n\n        } else {\n            reconcileBothCheckBox.setDisable(true);\n            reconcileIncomeExpenseCheckBox.setDisable(true);\n            disableReconcileCheckBox.setDisable(true);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/RemindersTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\n\nimport jgnash.uifx.Options;\n\n/**\n * Controller for Reminder Options.\n *\n * @author Craig Cavanaguh\n */\npublic class RemindersTabController {\n\n    @FXML\n    private CheckBox confirmOnDeleteCheckBox;\n\n    @FXML\n    private void initialize() {\n        confirmOnDeleteCheckBox.selectedProperty().bindBidirectional(Options.confirmOnDeleteReminderProperty());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/ReportTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport java.util.List;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.report.pdf.FontRegistry;\nimport jgnash.report.pdf.ReportFactory;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for Report Options.\n *\n * @author Craig Cavanaguh\n */\npublic class ReportTabController {\n\n    @FXML\n    private CheckBox rememberLastReportDateCheckBox;\n\n    @FXML\n    private ComboBox<String> headerFontComboBox;\n\n    @FXML\n    private ComboBox<String> monoFontComboBox;\n\n    @FXML\n    private ComboBox<String> proportionalFontComboBox;\n\n    @FXML\n    private void initialize() {\n        rememberLastReportDateCheckBox.selectedProperty().bindBidirectional(Options.restoreReportDateProperty());\n\n        new Thread(() -> {\n            final List<String> fonts = FontRegistry.getFontList();\n\n            JavaFXUtils.runLater(() -> {\n\n                monoFontComboBox.getItems().setAll(fonts);\n                monoFontComboBox.setValue(ReportFactory.getMonoFont());\n\n                proportionalFontComboBox.getItems().setAll(fonts);\n                proportionalFontComboBox.setValue(ReportFactory.getProportionalFont());\n\n                headerFontComboBox.getItems().setAll(fonts);\n                headerFontComboBox.setValue(ReportFactory.getHeaderFont());\n\n                monoFontComboBox.setOnAction(event -> ReportFactory.setMonoFont(monoFontComboBox.getValue()));\n\n                proportionalFontComboBox.setOnAction(event ->\n                        ReportFactory.setProportionalFont(proportionalFontComboBox.getValue()));\n\n                headerFontComboBox.setOnAction(event -> ReportFactory.setHeaderFont(headerFontComboBox.getValue()));\n            });\n        }).start();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/StartupShutdownTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.net.currency.CurrencyUpdateFactory;\nimport jgnash.net.security.UpdateFactory;\nimport jgnash.uifx.Options;\n\n/**\n * Controller for Startup and Shutdown options.\n *\n * @author Craig Cavanaugh\n */\npublic class StartupShutdownTabController {\n\n    @FXML\n    private CheckBox createBackupsCheckBox;\n\n    @FXML\n    private CheckBox removeOldBackupsCheckBox;\n\n    @FXML\n    private Spinner<Integer> backupCountSpinner;\n\n    @FXML\n    private CheckBox updateCurrencies;\n\n    @FXML\n    private CheckBox updateSecurities;\n\n    @FXML\n    private CheckBox openLastCheckBox;\n\n    @FXML\n    private CheckBox checkForUpdatesCheckBox;\n\n    @FXML\n    private void initialize() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        backupCountSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1000, 1, 1));\n\n        if (engine != null) {\n            backupCountSpinner.getValueFactory().setValue(engine.getRetainedBackupLimit());\n            createBackupsCheckBox.setSelected(engine.createBackups());\n            removeOldBackupsCheckBox.setSelected(engine.removeOldBackups());\n\n            backupCountSpinner.valueProperty().addListener((observable, oldValue, newValue)\n                    -> engine.setRetainedBackupLimit(newValue));\n\n            createBackupsCheckBox.selectedProperty().addListener((observable, oldValue, newValue)\n                    -> engine.setCreateBackups(newValue));\n\n            removeOldBackupsCheckBox.selectedProperty().addListener((observable, oldValue, newValue)\n                    -> engine.setRemoveOldBackups(newValue));\n        } else {\n            backupCountSpinner.setDisable(true);\n            createBackupsCheckBox.setDisable(true);\n            removeOldBackupsCheckBox.setDisable(true);\n            updateCurrencies.setDisable(true);\n            updateSecurities.setDisable(true);\n        }\n\n        updateSecurities.setSelected(UpdateFactory.getUpdateOnStartup());\n        updateCurrencies.setSelected(CurrencyUpdateFactory.getUpdateOnStartup());\n\n        updateSecurities.selectedProperty().addListener((observable, oldValue, newValue)\n                -> UpdateFactory.setUpdateOnStartup(newValue));\n\n        updateCurrencies.selectedProperty().addListener((observable, oldValue, newValue)\n                -> CurrencyUpdateFactory.setUpdateOnStartup(newValue));\n\n\n        openLastCheckBox.selectedProperty().bindBidirectional(Options.openLastProperty());\n\n        checkForUpdatesCheckBox.selectedProperty().bindBidirectional(Options.checkForUpdatesProperty());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/options/TransactionNumberDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.options;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.cell.TextFieldListCell;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Controller for editing the list of available transaction numbers.\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionNumberDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button upButton;\n\n    @FXML\n    private Button downButton;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private ListView<String> listView;\n\n    private List<String> returnValue = null;\n\n    final private IntegerProperty countProperty = new SimpleIntegerProperty();\n\n    final private IntegerProperty selectedIndexProperty = new SimpleIntegerProperty();\n\n    @FXML\n    private void initialize() {\n\n        // simplify binding\n        countProperty.bind(Bindings.size(listView.getItems()));\n        selectedIndexProperty.bind(listView.selectionModelProperty().get().selectedIndexProperty());\n\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        listView.setEditable(true);\n        listView.setCellFactory(TextFieldListCell.forListView());\n\n        listView.getItems().addAll(engine.getTransactionNumberList());\n        listView.getItems().add(\"\");    // and an empty string at the end\n\n        listView.setOnEditCommit(event -> {\n            listView.getItems().set(event.getIndex(), event.getNewValue());\n            processListItems();\n        });\n\n        upButton.disableProperty().bind(countProperty.lessThan(1)\n                .or(selectedIndexProperty.lessThan(1)));\n\n        downButton.disableProperty().bind(countProperty.lessThan(1)\n                .or(selectedIndexProperty.isEqualTo(countProperty.subtract(1)))\n                .or(selectedIndexProperty.lessThan(0)));\n    }\n\n    /**\n     * Remove all empty items from the list if they are empty except for the last list item.\n     * If the last list item is not empty, create a new empty item\n     */\n    private void processListItems() {\n        final List<String> items = listView.getItems();\n\n        items.removeIf(item -> item.trim().isEmpty() && items.indexOf(item) < items.size() - 1);\n\n        if (!items.get(items.size() - 1).isEmpty()) {\n            listView.getItems().add(\"\");    // and an empty string at the end\n        }\n    }\n\n    @FXML\n    private void handleUpAction() {\n        Collections.swap(listView.getItems(), selectedIndexProperty.get(), selectedIndexProperty.get() - 1);\n\n    }\n\n    @FXML\n    private void handleDownAction() {\n        Collections.swap(listView.getItems(), selectedIndexProperty.get(), selectedIndexProperty.get() + 1);\n    }\n\n    @FXML\n    private void handleOkayAction() {\n        final List<String> returnedItems = new ArrayList<>(listView.getItems());\n\n        returnedItems.removeIf(String::isEmpty);    // remove all empty strings\n\n        returnValue = returnedItems;\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    private Optional<List<String>> getItems() {\n        return Optional.ofNullable(returnValue);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    public static void showAndWait() {\n        final FXMLUtils.Pair<TransactionNumberDialogController> pair\n                = FXMLUtils.load(TransactionNumberDialogController.class.getResource(\"TransactionNumberDialog.fxml\"),\n                ResourceUtils.getString(\"Title.DefTranNum\"));\n\n        pair.getStage().showAndWait();\n\n        final Optional<List<String>> optional = pair.getController().getItems();\n\n        optional.ifPresent(strings -> {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.setTransactionNumberList(strings);\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/security/CreateModifySecuritiesController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.security;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.TextField;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.QuoteSource;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.CurrencyComboBox;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.control.QuoteSourceComboBox;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for creating and modifying securities.\n *\n * @author Craig Cavanaugh\n */\npublic class CreateModifySecuritiesController implements MessageListener {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button applyButton;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private ListView<SecurityNode> listView;\n\n    @FXML\n    private TextField symbolTextField;\n\n    @FXML\n    private TextField cusipTextField;\n\n    @FXML\n    private QuoteSourceComboBox quoteSourceComboBox;\n\n    @FXML\n    private TextField descriptionTextField;\n\n    @FXML\n    private IntegerTextField scaleTextField;\n\n    @FXML\n    private CurrencyComboBox reportedCurrencyComboBox;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final SimpleObjectProperty<SecurityNode> selectedSecurityNode = new SimpleObjectProperty<>();\n\n    @FXML\n    void initialize() {\n        selectedSecurityNode.bind(listView.getSelectionModel().selectedItemProperty());\n\n        deleteButton.disableProperty().bind(Bindings.isNull(selectedSecurityNode));\n\n        listView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> loadForm());\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.COMMODITY);\n\n        // disable button if the symbol, scale, or reported currency are not specified.\n        applyButton.disableProperty()\n                .bind(symbolTextField.textProperty().isEmpty()\n                        .or(scaleTextField.textProperty().isEmpty())\n                              .or(reportedCurrencyComboBox.valueProperty().isNull()));\n\n        new Thread(this::loadList).start();\n    }\n\n    private SecurityNode buildSecurityNode() {\n        final SecurityNode node = new SecurityNode(reportedCurrencyComboBox.getValue());\n\n        node.setDescription(descriptionTextField.getText());\n        node.setScale(scaleTextField.getInteger().byteValue());\n        node.setSymbol(symbolTextField.getText().trim());\n        node.setISIN(cusipTextField.getText());\n        node.setQuoteSource(quoteSourceComboBox.getValue());\n\n        return node;\n    }\n\n    private void loadForm() {\n        if (selectedSecurityNode.get() != null) {\n            final SecurityNode node = selectedSecurityNode.get();\n            symbolTextField.setText(node.getSymbol());\n            cusipTextField.setText(node.getISIN().trim());\n            descriptionTextField.setText(node.getDescription());\n            scaleTextField.setInteger((int) node.getScale());\n            reportedCurrencyComboBox.setValue(node.getReportedCurrencyNode());\n            quoteSourceComboBox.setValue(node.getQuoteSource());\n        }\n    }\n\n    private void clearForm() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        listView.getSelectionModel().clearSelection();\n        symbolTextField.setText(\"\");\n        cusipTextField.setText(\"\");\n        descriptionTextField.setText(\"\");\n        quoteSourceComboBox.setValue(QuoteSource.NONE);\n        reportedCurrencyComboBox.setValue(engine.getDefaultCurrency());\n\n        if (reportedCurrencyComboBox.getValue() != null) {  // null value is a miss configured file\n            scaleTextField.setInteger((int) reportedCurrencyComboBox.getValue().getScale());\n        }\n    }\n\n    private void loadList() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<SecurityNode> securityNodeList = engine.getSecurities();\n\n        JavaFXUtils.runLater(() -> {\n            listView.getItems().setAll(securityNodeList);\n            FXCollections.sort(listView.getItems());\n            clearForm();\n        });\n    }\n\n    @FXML\n    private void handleNewAction() {\n        clearForm();\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        if (!engine.removeSecurity(selectedSecurityNode.get())) {\n            StaticUIMethods.displayWarning(resources.getString(\"Message.Warn.CommodityInUse\"));\n        }\n\n        clearForm();\n    }\n\n    @FXML\n    private void handleCancelAction() {\n        clearForm();\n    }\n\n    @FXML\n    private void handleApplyAction() {\n\n        // always ensure a positive scale is entered\n        if (scaleTextField.getInteger() <= 0) {\n            scaleTextField.setInteger((int)reportedCurrencyComboBox.getValue().getScale());\n        }\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final SecurityNode newNode = buildSecurityNode();\n\n        if (selectedSecurityNode.get() != null) {\n            if (!engine.updateCommodity(selectedSecurityNode.get(), newNode)) {\n                StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.SecurityUpdate\",\n                        newNode.getSymbol()));\n            }\n        } else {\n            if (!engine.addSecurity(newNode)) {\n                StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.SecurityAdd\", newNode.getSymbol()));\n            }\n        }\n        clearForm();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @Override\n    public void messagePosted(Message message) {\n        if (message.getObject(MessageProperty.COMMODITY) instanceof SecurityNode) {\n            switch (message.getEvent()) {\n                case SECURITY_ADD:\n                case SECURITY_MODIFY:\n                case SECURITY_REMOVE:\n                    loadList();\n                    break;\n                default:\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/security/HistoricalImportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.security;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.ExecutionException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.concurrent.Task;\nimport javafx.concurrent.WorkerStateEvent;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ProgressBar;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.QuoteSource;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.net.security.UpdateFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.control.CheckListView;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Historical import controller.\n *\n * @author Craig Cavanaugh\n */\npublic class HistoricalImportController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button stopButton;\n\n    @FXML\n    private Button selectAllButton;\n\n    @FXML\n    private Button clearAllButton;\n\n    @FXML\n    private Button invertAllButton;\n\n    @FXML\n    private Label messageLabel;\n\n    @FXML\n    private Button startButton;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private CheckListView<SecurityNode> checkListView;\n\n    @FXML\n    private ProgressBar progressBar;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private Task<Void> updateTask = null;\n\n    private volatile boolean requestCancel = false;\n\n    private final BooleanProperty disableUI = new SimpleBooleanProperty();\n\n    @FXML\n    void initialize() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<SecurityNode> securityNodes = engine.getSecurities().stream()\n                .filter(securityNode -> securityNode.getQuoteSource() != QuoteSource.NONE)\n                .sorted().collect(Collectors.toList());\n\n        checkListView.getItems().addAll(securityNodes);\n\n        startDatePicker.setValue(LocalDate.now().minusMonths(1));\n\n        checkListView.disableProperty().bind(disableUI);\n        endDatePicker.disableProperty().bind(disableUI);\n        startDatePicker.disableProperty().bind(disableUI);\n        startButton.disableProperty().bind(disableUI);\n        selectAllButton.disableProperty().bind(disableUI);\n        clearAllButton.disableProperty().bind(disableUI);\n        invertAllButton.disableProperty().bind(disableUI);\n\n        stopButton.disableProperty().bind(disableUI.not());\n    }\n\n    @FXML\n    private void handleSelectAllAction() {\n        checkListView.checkAll();\n    }\n\n    @FXML\n    private void handleClearAllAction() {\n        checkListView.clearChecks();\n    }\n\n    @FXML\n    private void handleInvertSelectionAction() {\n        checkListView.toggleAll();\n    }\n\n    @FXML\n    private void handleStartAction() {\n        disableUI.set(true);\n\n        final DateTimeFormatter dateTimeFormatter = DateUtils.getShortDateFormatter();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        updateTask = new Task<>() {\n            @Override\n            protected Void call() {\n                final LocalDate startDate = startDatePicker.getValue();\n                final LocalDate endDate = endDatePicker.getValue();\n\n                final Map<SecurityNode, List<SecurityHistoryNode>> historyMap = new HashMap<>();\n\n                // create a defensive copy\n                final List<SecurityNode> securityNodes =\n                        new ArrayList<>(checkListView.getCheckedItems());\n\n                // need to determine the total count\n                long historyCount = 0;\n\n                // Collect and count the number of nodes\n                for (final SecurityNode securityNode : securityNodes) {\n                    updateMessage(ResourceUtils.getString(\"Message.DownloadingX\", securityNode.getSymbol()));\n\n                    try {\n                        final List<SecurityHistoryNode> historyNodes =\n                                UpdateFactory.downloadHistory(securityNode, startDate, endDate);\n\n                        Collections.reverse(historyNodes);  // reverse the sort order\n\n                        historyCount += historyNodes.size();\n\n                        historyMap.put(securityNode, historyNodes);\n                    } catch (final IllegalArgumentException iae) {\n                        updateMessage(ResourceUtils.getString(\"Message.Error.DataSupplierToken\",\n                                securityNode.getSymbol()));\n\n                        this.cancel();\n                    }\n                }\n\n                // need to track the total processed count\n                long processedHistory = 0;\n\n                for (final Map.Entry<SecurityNode, List<SecurityHistoryNode>> entry : historyMap.entrySet()) {\n                    if (!requestCancel) {\n                        for (final SecurityHistoryNode historyNode : entry.getValue()) {\n                            if (!requestCancel) {\n                                engine.addSecurityHistory(entry.getKey(), historyNode);\n                                updateProgress(++processedHistory, historyCount);\n\n                                updateMessage(ResourceUtils.getString(\"Message.UpdatedPriceDate\", entry.getKey().getSymbol(),\n                                        dateTimeFormatter.format(historyNode.getLocalDate())));\n                            }\n                        }\n                    }\n                }\n\n                updateMessage(\"\");\n\n                return null;\n            }\n        };\n\n        updateTask.addEventHandler(WorkerStateEvent.WORKER_STATE_SUCCEEDED, event -> taskComplete());\n        updateTask.addEventHandler(WorkerStateEvent.WORKER_STATE_CANCELLED, event -> taskComplete());\n        updateTask.addEventHandler(WorkerStateEvent.WORKER_STATE_FAILED, event -> taskComplete());\n\n        progressBar.progressProperty().bind(updateTask.progressProperty());\n        messageLabel.textProperty().bind(updateTask.messageProperty());\n\n        final Thread thread = new Thread(updateTask);\n        thread.setDaemon(true);\n        thread.start();\n    }\n\n    private void taskComplete() {\n        requestCancel = false;\n\n        progressBar.progressProperty().unbind();\n        progressBar.progressProperty().set(0);\n\n        messageLabel.textProperty().unbind();\n        updateTask = null;\n\n        checkListView.clearChecks();\n\n        disableUI.set(false);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        handleStopAction();\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleStopAction() {\n        if (updateTask != null) {\n            requestCancel = true;\n\n            try {\n                updateTask.get();    // wait for a graceful exit\n            } catch (final ExecutionException | InterruptedException e) {\n                Logger.getLogger(HistoricalImportController.class.getName()).log(Level.SEVERE,\n                        e.getLocalizedMessage(), e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/dialog/security/SecurityHistoryController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.dialog.security;\n\nimport java.math.BigDecimal;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.logging.Logger;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.QuoteSource;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.net.security.UpdateFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.BigDecimalTableCell;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.control.SecurityComboBox;\nimport jgnash.uifx.control.SecurityHistoryEventTypeComboBox;\nimport jgnash.uifx.control.SecurityNodeAreaChart;\nimport jgnash.uifx.control.ShortDateTableCell;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Security history controller.\n *\n * @author Craig Cavanaugh\n */\npublic class SecurityHistoryController implements MessageListener {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private SecurityHistoryEventTypeComboBox securityEventTypeComboBox;\n\n    @FXML\n    private DatePickerEx eventDatePicker;\n\n    @FXML\n    private DecimalTextField eventValueTextField;\n\n    @FXML\n    private Button deleteEventButton;\n\n    @FXML\n    private Button addEventButton;\n\n    @FXML\n    private StackPane chartPane;\n\n    @FXML\n    private Button addPriceButton;\n\n    @FXML\n    private Button updatePriceButton;\n\n    @FXML\n    private Button updateEventButton;\n\n    @FXML\n    private TableView<SecurityHistoryNode> priceTableView;\n\n    @FXML\n    private TableView<SecurityHistoryEvent> eventTableView;\n\n    @FXML\n    private SecurityComboBox securityComboBox;\n\n    @FXML\n    private DatePickerEx historyDatePicker;\n\n    @FXML\n    private DecimalTextField closeTextField;\n\n    @FXML\n    private IntegerTextField volumeTextField;\n\n    @FXML\n    private DecimalTextField highTextField;\n\n    @FXML\n    private DecimalTextField lowTextField;\n\n    @FXML\n    private Button deletePriceButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private SecurityNodeAreaChart chart;\n\n    private final SimpleObjectProperty<SecurityHistoryNode> selectedSecurityHistoryNode = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<SecurityHistoryEvent> selectedSecurityHistoryEvent = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<SecurityNode> selectedSecurityNode = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<NumberFormat> numberFormat = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<QuoteSource> quoteSource = new SimpleObjectProperty<>();\n\n    private final ObservableList<SecurityHistoryNode> observableHistoryNodes = FXCollections.observableArrayList();\n\n    private final SortedList<SecurityHistoryNode> sortedHistoryList = new SortedList<>(observableHistoryNodes);\n\n    private final ObservableList<SecurityHistoryEvent> observableHistoryEventList = FXCollections.observableArrayList();\n\n    private final SortedList<SecurityHistoryEvent> sortedHistoryEventList = new SortedList<>(observableHistoryEventList);\n\n    @FXML\n    void initialize() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        numberFormat.set(NumericFormats.getShortCommodityFormat(engine.getDefaultCurrency()));\n\n        selectedSecurityHistoryNode.bind(priceTableView.getSelectionModel().selectedItemProperty());\n        selectedSecurityNode.bind(securityComboBox.getSelectionModel().selectedItemProperty());\n\n        deletePriceButton.disableProperty().bind(Bindings.isNull(selectedSecurityHistoryNode));\n\n        selectedSecurityHistoryEvent.bind(eventTableView.getSelectionModel().selectedItemProperty());\n        deleteEventButton.disableProperty().bind(Bindings.isNull(selectedSecurityHistoryEvent));\n\n        // Disabled the update button if a security is not selected, or it does not have a quote source\n        updatePriceButton.disableProperty().bind(Bindings.or(Bindings.isNull(selectedSecurityNode),\n                Bindings.equal(QuoteSource.NONE, quoteSource)));\n\n        // Disabled the update button if a security is not selected, or it does not have a quote source\n        updateEventButton.disableProperty().bind(Bindings.or(Bindings.isNull(selectedSecurityNode),\n                Bindings.equal(QuoteSource.NONE, quoteSource)));\n\n        // Can't add if a security is not selected\n        addPriceButton.disableProperty().bind(Bindings.isNull(selectedSecurityNode));\n\n        // Can't add if a security is not selected and a value is not set\n        addEventButton.disableProperty().bind(Bindings.isNull(selectedSecurityNode)\n                .or(Bindings.isEmpty(eventValueTextField.textProperty())));\n\n\n        priceTableView.setTableMenuButtonVisible(false);\n        priceTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        final TableColumn<SecurityHistoryNode, LocalDate> priceDateColumn = new TableColumn<>(resources.getString(\"Column.Date\"));\n        priceDateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getLocalDate()));\n        priceDateColumn.setCellFactory(cell -> new ShortDateTableCell<>());\n        priceTableView.getColumns().add(priceDateColumn);\n\n        final TableColumn<SecurityHistoryNode, BigDecimal> priceCloseColumn = new TableColumn<>(resources.getString(\"Column.Close\"));\n        priceCloseColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getPrice()));\n        priceCloseColumn.setCellFactory(cell -> new BigDecimalTableCell<>(numberFormat));\n        priceTableView.getColumns().add(priceCloseColumn);\n\n        final TableColumn<SecurityHistoryNode, BigDecimal> priceLowColumn = new TableColumn<>(resources.getString(\"Column.Low\"));\n        priceLowColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getLow()));\n        priceLowColumn.setCellFactory(cell -> new BigDecimalTableCell<>(numberFormat));\n        priceTableView.getColumns().add(priceLowColumn);\n\n        final TableColumn<SecurityHistoryNode, BigDecimal> priceHighColumn = new TableColumn<>(resources.getString(\"Column.High\"));\n        priceHighColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getHigh()));\n        priceHighColumn.setCellFactory(cell -> new BigDecimalTableCell<>(numberFormat));\n        priceTableView.getColumns().add(priceHighColumn);\n\n        final TableColumn<SecurityHistoryNode, Long> priceVolumeColumn = new TableColumn<>(resources.getString(\"Column.Volume\"));\n        priceVolumeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getVolume()));\n        priceVolumeColumn.setCellFactory(cell -> new LongFormatTableCell());\n        priceTableView.getColumns().add(priceVolumeColumn);\n\n        priceTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        sortedHistoryList.comparatorProperty().bind(priceTableView.comparatorProperty());\n\n        priceTableView.setItems(sortedHistoryList);\n\n        final TableColumn<SecurityHistoryEvent, LocalDate> eventDateColumn = new TableColumn<>(resources.getString(\"Column.Date\"));\n        eventDateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getDate()));\n        eventDateColumn.setCellFactory(cell -> new ShortDateTableCell<>());\n        eventTableView.getColumns().add(eventDateColumn);\n\n        final TableColumn<SecurityHistoryEvent, String> eventActionColumn = new TableColumn<>(resources.getString(\"Column.Event\"));\n        eventActionColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getType().toString()));\n        eventTableView.getColumns().add(eventActionColumn);\n\n        final NumberFormat decimalFormat = NumberFormat.getInstance();\n        if (decimalFormat instanceof DecimalFormat) {\n            decimalFormat.setMinimumFractionDigits(MathConstants.SECURITY_PRICE_ACCURACY);\n            decimalFormat.setMaximumFractionDigits(MathConstants.SECURITY_PRICE_ACCURACY);\n        }\n\n        final TableColumn<SecurityHistoryEvent, BigDecimal> eventValueColumn = new TableColumn<>(resources.getString(\"Column.Value\"));\n        eventValueColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getValue()));\n        eventValueColumn.setCellFactory(cell -> new BigDecimalTableCell<>(decimalFormat));\n        eventTableView.getColumns().add(eventValueColumn);\n\n        eventTableView.setTableMenuButtonVisible(false);\n        eventTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        eventTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        sortedHistoryEventList.comparatorProperty().bind(eventTableView.comparatorProperty());\n\n        eventTableView.setItems(sortedHistoryEventList);\n\n        eventValueTextField.scaleProperty().set(MathConstants.SECURITY_PRICE_ACCURACY);\n\n        chart = new SecurityNodeAreaChart();\n        chart.securityNodeProperty().bind(selectedSecurityNode);\n\n        chartPane.getChildren().addAll(chart);\n\n        securityComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                numberFormat.set(NumericFormats.getShortCommodityFormat(newValue.getReportedCurrencyNode()));\n\n                closeTextField.scaleProperty().set(newValue.getScale());\n                lowTextField.scaleProperty().set(newValue.getScale());\n                highTextField.scaleProperty().set(newValue.getScale());\n\n                quoteSource.set(newValue.getQuoteSource());\n\n                JavaFXUtils.runLater(this::loadTables);\n            }\n        });\n\n        selectedSecurityHistoryNode.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                loadPriceForm();\n            } else {\n                clearPriceForm();\n            }\n        });\n\n        selectedSecurityHistoryEvent.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                loadEventForm();\n            } else {\n                clearEventForm();\n            }\n        });\n\n        // Install a listener to unregister from the message bus when the window closes\n        parent.addListener((observable, oldValue, scene) -> {\n            if (scene != null) {\n                scene.windowProperty().get().addEventHandler(WindowEvent.WINDOW_HIDING, event -> {\n                    Logger.getLogger(SecurityHistoryController.class.getName()).info(\"Unregistered from the message bus\");\n                    MessageBus.getInstance().unregisterListener(SecurityHistoryController.this, MessageChannel.COMMODITY);\n                });\n            }\n        });\n\n        JavaFXUtils.runLater(()\n                -> MessageBus.getInstance().registerListener(SecurityHistoryController.this, MessageChannel.COMMODITY));\n    }\n\n    private void loadPriceForm() {\n        historyDatePicker.setValue(selectedSecurityHistoryNode.get().getLocalDate());\n        closeTextField.setDecimal(selectedSecurityHistoryNode.get().getPrice());\n        lowTextField.setDecimal(selectedSecurityHistoryNode.get().getLow());\n        highTextField.setDecimal(selectedSecurityHistoryNode.get().getHigh());\n        volumeTextField.setLong(selectedSecurityHistoryNode.get().getVolume());\n    }\n\n    private void loadEventForm() {\n        JavaFXUtils.runLater(() -> {\n            eventDatePicker.setValue(selectedSecurityHistoryEvent.get().getDate());\n            eventValueTextField.setDecimal(selectedSecurityHistoryEvent.get().getValue());\n            securityEventTypeComboBox.setValue(selectedSecurityHistoryEvent.get().getType());\n        });\n\n    }\n\n    private void clearPriceForm() {\n        historyDatePicker.setValue(LocalDate.now());\n        closeTextField.setDecimal(BigDecimal.ZERO);\n        volumeTextField.setText(null);\n        lowTextField.setDecimal(BigDecimal.ZERO);\n        highTextField.setDecimal(BigDecimal.ZERO);\n    }\n\n    private void clearEventForm() {\n        JavaFXUtils.runLater(() -> {\n            eventDatePicker.setValue(LocalDate.now());\n            eventValueTextField.setDecimal(BigDecimal.ZERO);\n        });\n    }\n\n    @FXML\n    private void handleDeletePriceAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<SecurityHistoryNode> historyNodes = new ArrayList<>(priceTableView.getSelectionModel().getSelectedItems());\n\n        Collections.reverse(historyNodes);  // work backwards through the deletion list\n\n        for (final SecurityHistoryNode historyNode : historyNodes) {\n            engine.removeSecurityHistory(selectedSecurityNode.get(), historyNode.getLocalDate());\n        }\n    }\n\n    @FXML\n    private void handleClearPriceAction() {\n        priceTableView.getSelectionModel().clearSelection();\n        clearPriceForm();\n    }\n\n    @FXML\n    private void handleAddPriceAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final SecurityHistoryNode history = new SecurityHistoryNode(historyDatePicker.getValue(), closeTextField.getDecimal(),\n                volumeTextField.getLong(), highTextField.getDecimal(), lowTextField.getDecimal());\n\n        engine.addSecurityHistory(selectedSecurityNode.get(), history);\n\n        clearPriceForm();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleOnlineUpdate() {\n        if (!UpdateFactory.updateOne(selectedSecurityNode.get())) {\n            StaticUIMethods.displayWarning(ResourceUtils.getString(\"Message.Error.SecurityUpdate\",\n                    selectedSecurityNode.get().getSymbol()));\n        }\n    }\n\n    @FXML\n    private void handleDeleteEventAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<SecurityHistoryEvent> events = new ArrayList<>(eventTableView.getSelectionModel().getSelectedItems());\n\n        Collections.reverse(events);  // work backwards through the deletion list\n\n        for (final SecurityHistoryEvent securityHistoryEvent : events) {\n            engine.removeSecurityHistoryEvent(selectedSecurityNode.get(), securityHistoryEvent);\n        }\n    }\n\n    @FXML\n    private void handleAddEventAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final SecurityHistoryEvent event = new SecurityHistoryEvent(securityEventTypeComboBox.getValue(),\n                eventDatePicker.getValue(), eventValueTextField.getDecimal());\n\n        engine.addSecurityHistoryEvent(selectedSecurityNode.get(), event);\n\n        clearEventForm();\n    }\n\n    @FXML\n    private void handleOnlineEventUpdate() {\n        UpdateFactory.updateSecurityEvents(selectedSecurityNode.get());\n    }\n\n    @FXML\n    private void handleClearEventAction() {\n        eventTableView.getSelectionModel().clearSelection();\n        clearEventForm();\n    }\n\n    private void loadTables() {\n        priceTableView.getSelectionModel().clearSelection();\n        observableHistoryNodes.setAll(securityComboBox.getValue().getHistoryNodes());\n        priceTableView.scrollTo(observableHistoryNodes.size() - 1);\n\n\n        final List<SecurityHistoryEvent> events = new ArrayList<>(securityComboBox.getValue().getHistoryEvents());\n        Collections.sort(events);   // events are not sorted\n\n        eventTableView.getSelectionModel().clearSelection();\n        observableHistoryEventList.setAll(events);\n        eventTableView.scrollTo(observableHistoryEventList.size() - 1);\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        final CommodityNode eventNode = message.getObject(MessageProperty.COMMODITY);\n\n        if (eventNode.equals(selectedSecurityNode.get())) {\n            switch (message.getEvent()) {\n                case SECURITY_HISTORY_ADD:\n                case SECURITY_HISTORY_REMOVE:\n                case SECURITY_HISTORY_EVENT_ADD:\n                case SECURITY_HISTORY_EVENT_REMOVE:\n                    JavaFXUtils.runLater(this::loadTables);\n                    JavaFXUtils.runLater(chart::update);\n                    break;\n                default:\n            }\n        }\n    }\n\n    @FXML\n    private void handleRemoveWeekendsAction() {\n        if (StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.Confirm\"),\n                resources.getString(\"Message.ConfirmSecurityHistoryDelete\")).getButtonData() == ButtonBar.ButtonData.YES) {\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.removeSecurityHistoryByDayOfWeek(selectedSecurityNode.get(),\n                    Arrays.asList(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY));\n        }\n    }\n\n    @FXML\n    private void handleKeepFridaysOnlyAction() {\n        if (StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.Confirm\"),\n                resources.getString(\"Message.ConfirmSecurityHistoryDelete\")).getButtonData() == ButtonBar.ButtonData.YES) {\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.removeSecurityHistoryByDayOfWeek(selectedSecurityNode.get(),\n                    Arrays.asList(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY,\n                            DayOfWeek.SATURDAY, DayOfWeek.SUNDAY));\n        }\n    }\n\n    @FXML\n    private void handleKeepMondaysOnlyAction() {\n        if (StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.Confirm\"),\n                resources.getString(\"Message.ConfirmSecurityHistoryDelete\")).getButtonData() == ButtonBar.ButtonData.YES) {\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.removeSecurityHistoryByDayOfWeek(selectedSecurityNode.get(),\n                    Arrays.asList(DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY,\n                            DayOfWeek.SATURDAY, DayOfWeek.SUNDAY));\n        }\n    }\n\n    private static class LongFormatTableCell extends TableCell<SecurityHistoryNode, Long> {\n\n        private final NumberFormat volumeFormat = NumberFormat.getIntegerInstance();\n\n        LongFormatTableCell() {\n            setStyle(\"-fx-alignment: center-right;\"); // Right align\n        }\n\n        @Override\n        protected void updateItem(final Long amount, final boolean empty) {\n            super.updateItem(amount, empty);  // required\n\n            if (!empty && amount != null) {\n                setText(volumeFormat.format(amount));\n            } else {\n                setText(null);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/net/NetworkAuthenticator.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.net;\n\nimport java.net.PasswordAuthentication;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.scene.Node;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ButtonType;\nimport javafx.scene.control.Dialog;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.PasswordField;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.GridPane;\nimport javafx.util.Pair;\n\nimport jgnash.net.AbstractAuthenticator;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * An Authenticator that will pop up a dialog and ask for http authentication\n * info if it has not assigned. This does not make authentication information\n * permanent. That must be done using the options configuration for http connect\n *\n * @author Craig Cavanaugh\n */\npublic class NetworkAuthenticator extends AbstractAuthenticator {\n\n    @Override\n    protected PasswordAuthentication getPasswordAuthentication() {\n        final Preferences auth = Preferences.userRoot().node(NODEHTTP);\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final char[][] pass = {null};\n        final String[] user = new String[1];\n\n        // get the password\n        if (auth.get(HTTPPASS, null) != null && !auth.get(HTTPPASS, null).isEmpty()) {\n            pass[0] = auth.get(HTTPPASS, null).toCharArray();\n        }\n\n        // get the user\n        user[0] = auth.get(HTTPUSER, null);\n        if (user[0] != null) {\n            if (user[0].length() <= 0) {\n                user[0] = null;\n            }\n        }\n\n        // if either returns null, pop a dialog\n        if (user[0] == null || pass[0] == null) {\n\n            final Dialog<Pair<String, String>> dialog = new Dialog<>();\n            dialog.setTitle(resources.getString(\"Title.HTTPProxy\"));\n            dialog.setHeaderText(resources.getString(\"Message.EnterNetworkAuth\"));\n\n            // Set the button types.\n            final ButtonType loginButtonType = new ButtonType(resources.getString(\"Button.Ok\"), ButtonBar.ButtonData.OK_DONE);\n\n            ThemeManager.applyStyleSheets(dialog.getDialogPane());\n\n            dialog.getDialogPane().getStyleClass().addAll(\"dialog\");\n            dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);\n\n            // Create the username and password labels and fields.\n            final GridPane grid = new GridPane();\n            grid.getStyleClass().addAll(\"form\");\n\n            final TextField userNameField = new TextField();\n            final PasswordField passwordField = new PasswordField();\n\n            grid.add(new Label(resources.getString(\"Label.UserName\")), 0, 0);\n            grid.add(userNameField, 1, 0);\n            grid.add(new Label(resources.getString(\"Label.Password\")), 0, 1);\n            grid.add(passwordField, 1, 1);\n\n            // Enable/Disable login button depending on whether a username was entered.\n            final Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);\n            loginButton.setDisable(true);\n\n            // bind the button, must not be empty\n            loginButton.disableProperty().bind(userNameField.textProperty().isEmpty());\n\n            dialog.getDialogPane().setContent(grid);\n\n            // Request focus on the username field by default.\n            JavaFXUtils.runLater(userNameField::requestFocus);\n\n            dialog.setResultConverter(dialogButton -> {\n                if (dialogButton == loginButtonType) {\n                    return new Pair<>(userNameField.getText(), passwordField.getText());\n                }\n                return null;\n            });\n\n            final Optional<Pair<String, String>> result = dialog.showAndWait();\n\n            result.ifPresent(usernamePassword -> {\n                user[0] = usernamePassword.getKey();\n                pass[0] = usernamePassword.getValue().toCharArray();\n            });\n        }\n        \n        return new PasswordAuthentication(user[0], pass[0] != null ? pass[0] : new char[0]);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/AbstractSumByTypeReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.text.MessageFormat;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.MathConstants;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.report.table.Row;\nimport jgnash.report.table.SortOrder;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.time.Period;\nimport jgnash.util.NotNull;\n\n/**\n * Abstract Report that groups and sums by {@code AccountGroup} and has a line for a global sum. and cross tabulates\n * all rows.\n *\n * @author Craig Cavanaugh\n * @author Michael Mueller\n * @author David Robertson\n * @author Aleksey Trufanov\n * @author Vincent Frison\n * @author Klemen Zagar\n */\npublic abstract class AbstractSumByTypeReport extends Report {\n\n    private boolean runningTotal = true;\n\n    final ArrayList<LocalDate> startDates = new ArrayList<>();\n\n    final ArrayList<LocalDate> endDates = new ArrayList<>();\n\n    private final ArrayList<String> dateLabels = new ArrayList<>();\n\n    private final Map<Account, BigDecimal> percentileMap = new HashMap<>();\n\n    private boolean addCrossTabColumn = false;\n\n    private boolean addPercentileColumn = false;\n\n    private String subTitle = \"\";\n\n    private String title = \"\";\n\n    private boolean showFullAccountPath = false;\n\n    private SortOrder sortOrder = SortOrder.BY_NAME;\n\n    private Period reportPeriod = Period.MONTHLY;\n\n    /**\n     * Returns a list of AccountGroup that will be reported on\n     *\n     * @return List of AccountGroup\n     */\n    @NotNull\n    protected abstract List<AccountGroup> getAccountGroups();\n\n    /**\n     * Returns the reporting period\n     *\n     * @return returns a Monthly period unless overridden\n     */\n    private Period getReportPeriod() {\n        return reportPeriod;\n    }\n\n    void setReportPeriod(final Period reportPeriod) {\n        this.reportPeriod = reportPeriod;\n    }\n\n    void setSortOrder(@NotNull final SortOrder sortOrder) {\n        this.sortOrder = sortOrder;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    ReportModel createReportModel(final LocalDate startDate, final LocalDate endDate,\n                                  final boolean hideZeroBalanceAccounts) {\n\n        percentileMap.clear();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        // update the subtitle\n        final MessageFormat format = new MessageFormat(rb.getString(\"Pattern.DateRange\"));\n        subTitle = format.format(new Object[]{DateUtils.asDate(startDate), DateUtils.asDate(endDate)});\n\n        // generate the required date and label arrays\n        updateResolution(startDate, endDate);\n\n        final CurrencyNode baseCurrency = engine.getDefaultCurrency();\n\n        List<Account> accounts = new ArrayList<>();\n\n        for (final AccountGroup group : getAccountGroups()) {\n            accounts.addAll(getAccountList(AccountType.getAccountTypes(group)));\n        }\n\n        // remove any account that will report a zero balance for all periods\n        if (hideZeroBalanceAccounts) {\n            final Iterator<Account> i = accounts.iterator();\n\n            while (i.hasNext()) {\n                final Account account = i.next();\n                boolean remove = true;\n\n                if (runningTotal) {\n                    for (final LocalDate date : startDates) {\n                        if (account.getBalance(date).compareTo(BigDecimal.ZERO) != 0) {\n                            remove = false;\n                            break;\n                        }\n                    }\n\n                    for (final LocalDate date : endDates) {\n                        if (account.getBalance(date).compareTo(BigDecimal.ZERO) != 0) {\n                            remove = false;\n                            break;\n                        }\n                    }\n\n\n                } else {\n                    for (int j = 0; j < startDates.size(); j++) {\n                        if (account.getBalance(startDates.get(j), endDates.get(j)).compareTo(BigDecimal.ZERO) != 0) {\n                            remove = false;\n                            break;\n                        }\n                    }\n                }\n                if (remove) {\n                    i.remove();\n                }\n            }\n        }\n\n        switch (sortOrder) {    // sort the accounts\n            case BY_NAME:\n                accounts.sort(showFullAccountPath ? Comparators.getAccountByPathName() : Comparators.getAccountByName());\n                break;\n            case BY_BALANCE:\n                accounts.sort(Comparators.getAccountByBalance(startDate, endDate, baseCurrency, true));\n                break;\n            default:\n                accounts.sort(Comparators.getAccountByName());\n        }\n\n        // cross tabulate account percentages by group\n        if (addPercentileColumn) {\n            for (final AccountGroup group : getAccountGroups()) {\n\n                // sum the group\n                BigDecimal groupTotal = BigDecimal.ZERO;\n                for (final Account a : accounts) {\n                    if (a.getAccountType().getAccountGroup() == group) {\n                        groupTotal = groupTotal.add(a.getBalance(startDate, endDate, baseCurrency));\n                    }\n                }\n\n                // calculate the percentage\n                for (final Account a : accounts) {\n                    if (a.getAccountType().getAccountGroup() == group) {\n\n                        BigDecimal sum = a.getBalance(startDate, endDate, baseCurrency);\n                        percentileMap.put(a, sum.divide(groupTotal, MathConstants.mathContext));\n                    }\n                }\n            }\n        }\n\n        final ReportModel model = new ReportModel(baseCurrency);\n        model.addAccounts(accounts);\n\n        return model;\n    }\n\n    private void updateResolution(final LocalDate startDate, final LocalDate endDate) {\n\n        final DateTimeFormatter dateFormat = DateUtils.getShortDateFormatter();\n\n\n        startDates.clear();\n        endDates.clear();\n        dateLabels.clear();\n\n        LocalDate start = startDate;\n        LocalDate end = startDate;\n\n        switch (getReportPeriod()) {\n            case YEARLY:\n                while (start.isBefore(endDate)) {\n                    startDates.add(start);\n                    end = DateUtils.getLastDayOfTheYear(start);\n                    endDates.add(end);\n                    dateLabels.add(String.valueOf(start.getYear()));\n                    start = end.plusDays(1);\n                }\n                break;\n            case QUARTERLY:\n                int i = DateUtils.getQuarterNumber(start) - 1;\n                while (end.isBefore(endDate)) {\n                    startDates.add(start);\n                    end = DateUtils.getLastDayOfTheQuarter(start);\n                    endDates.add(end);\n                    dateLabels.add(start.getYear() + \"-Q\" + (1 + i++ % 4));\n                    start = end.plusDays(1);\n                }\n                break;\n            case MONTHLY:   // default is monthly\n            default:\n                endDates.addAll(DateUtils.getLastDayOfTheMonths(startDate, endDate));\n                startDates.addAll(DateUtils.getFirstDayOfTheMonths(startDate, endDate));\n\n                startDates.set(0, startDate);   // force the start date\n\n                if (runningTotal) {\n                    for (final LocalDate date : endDates) {\n                        dateLabels.add(dateFormat.format(date));\n                    }\n                } else {\n                    for (int j = 0; j < startDates.size(); j++) {\n                        dateLabels.add(dateFormat.format(startDates.get(j)) + \" - \" + dateFormat.format(endDates.get(j)));\n                    }\n                }\n\n                break;\n        }\n\n        assert startDates.size() == endDates.size() && startDates.size() == dateLabels.size();\n\n        // adjust label for global end date\n        if (endDates.get(startDates.size() - 1).compareTo(endDate) > 0) {\n            endDates.set(endDates.size() - 1, endDate);\n        }\n    }\n\n    private static List<Account> getAccountList(final Set<AccountType> types) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        return engine.getAccountList().stream().\n                filter(a -> types.contains(a.getAccountType())).distinct().sorted().collect(Collectors.toList());\n    }\n\n    void setRunningTotal(final boolean runningTotal) {\n        this.runningTotal = runningTotal;\n    }\n\n    void setAddCrossTabColumn(final boolean addCrossTabColumn) {\n        this.addCrossTabColumn = addCrossTabColumn;\n    }\n\n    void setAddPercentileColumn(final boolean addPercentileColumn) {\n        this.addPercentileColumn = addPercentileColumn;\n    }\n\n    private boolean isShowFullAccountPath() {\n        return showFullAccountPath;\n    }\n\n    void setShowFullAccountPath(boolean showFullAccountPath) {\n        this.showFullAccountPath = showFullAccountPath;\n    }\n\n    protected class ReportModel extends AbstractReportTableModel {\n\n        private final List<Row<?>> rowList = new ArrayList<>();\n\n        private final CurrencyNode baseCurrency;\n\n        private final ResourceBundle rb = ResourceUtils.getBundle();\n\n        ReportModel(final CurrencyNode currency) {\n            this.baseCurrency = currency;\n        }\n\n        @Override\n        public String getTitle() {\n            return AbstractSumByTypeReport.this.title;\n        }\n\n        @Override\n        public String getSubTitle() {\n            return AbstractSumByTypeReport.this.subTitle;\n        }\n\n        /**\n         * Returns the legend for the grand total\n         *\n         * @return report name\n         */\n        @Override\n        public String getGrandTotalLegend() {\n            return AbstractSumByTypeReport.this.getGrandTotalLegend();\n        }\n\n        /**\n         * Returns the general label for the group footer\n         *\n         * @return footer label\n         */\n        public String getGroupFooterLabel() {\n            return AbstractSumByTypeReport.this.getGroupFooterLabel();\n        }\n\n        void addAccounts(final Collection<Account> accounts) {\n            accounts.forEach(this::addAccount);\n        }\n\n        /**\n         * Supports manual addition of a report row\n         *\n         * @param row the Row to add\n         */\n        void addRow(final Row<?> row) {\n            rowList.add(row);\n        }\n\n        void addAccount(final Account account) {\n            rowList.add(new AccountRow(account));\n        }\n\n        @Override\n        public int getRowCount() {\n            return rowList.size();\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            return columnIndex != 0;    // fixed width if not the account column\n        }\n\n        /**\n         * Returns the number of additional columns added by report options\n         *\n         * @return extra column count\n         */\n        private int getExtraColumnCount() {\n            return (addPercentileColumn ? 1 : 0) + (addCrossTabColumn ? 1 : 0);\n        }\n\n        @Override\n        public int getColumnCount() {\n            return startDates.size() + 2 + getExtraColumnCount();\n        }\n\n        @Override\n        public Object getValueAt(final int rowIndex, final int columnIndex) {\n            if (!rowList.isEmpty()) {\n                return rowList.get(rowIndex).getValueAt(columnIndex);\n            }\n\n            return null;\n        }\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return baseCurrency;\n        }\n\n        @Override\n        public Class<?> getColumnClass(final int columnIndex) {\n            if (columnIndex == 0 || columnIndex == getColumnCount() - 1) { // accounts and group column\n                return String.class;\n            }\n\n            return BigDecimal.class;\n        }\n\n        @Override\n        public String getColumnName(final int columnIndex) {\n\n            if (isCrossTabColumn(columnIndex)) {\n                return \"\";\n            }\n\n            if (isPercentileColumn(columnIndex)) {\n                return rb.getString(\"Column.Percentile\");\n            }\n\n            if (columnIndex == 0) {\n                return rb.getString(\"Column.Account\");\n            } else if (columnIndex == getColumnCount() - 1) {   // type / group\n                return \"Type\";\n            }\n\n            return dateLabels.get(columnIndex - 1);\n        }\n\n        @Override\n        public ColumnStyle getColumnStyle(final int columnIndex) {\n\n            if (isPercentileColumn(columnIndex)) {\n                return ColumnStyle.PERCENTAGE;\n            }\n\n            if (columnIndex == 0) { // accounts column\n                return ColumnStyle.STRING;\n            } else if (columnIndex == getColumnCount() - 1) { // group column\n                return ColumnStyle.GROUP;\n            }\n            return ColumnStyle.BALANCE_WITH_SUM_AND_GLOBAL;\n        }\n\n        private boolean isPercentileColumn(final int columnIndex) {\n            if (addPercentileColumn) {\n                return columnIndex == getColumnCount() - 2; // last column\n            }\n            return false;\n        }\n\n        private boolean isCrossTabColumn(final int columnIndex) {\n            if (addCrossTabColumn && addPercentileColumn) {\n                return columnIndex == getColumnCount() - 3; // 2nd to last column\n            } else if (addCrossTabColumn) {\n                return columnIndex == getColumnCount() - 2; // last column when percentages are not displayed\n            }\n\n            return false;\n        }\n\n        private class AccountRow extends Row<Account> {\n\n            AccountRow(final Account account) {\n                super(account);\n            }\n\n            @Override\n            public Object getValueAt(final int columnIndex) {\n\n                // check for cross tabulation column and do the math\n                if (isCrossTabColumn(columnIndex)) {\n                    BigDecimal sum = BigDecimal.ZERO;\n\n                    for (int i = 1; i < getColumnCount() - 1 - getExtraColumnCount(); i++) {\n                        sum = sum.add((BigDecimal) getValueAt(i));\n                    }\n                    return sum;\n                }\n\n                if (isPercentileColumn(columnIndex)) {\n                   return percentileMap.get(getValue());\n                }\n\n                if (columnIndex == 0) { // account column\n                    return isShowFullAccountPath() ? getValue().getPathName() : getValue().getName();\n                } else if (columnIndex == getColumnCount() - 1) { // group column\n                    return getValue().getAccountType().getAccountGroup().toString();\n                } else if (columnIndex > 0 && columnIndex <= startDates.size()) {\n                    if (runningTotal) {\n                        return getValue().getBalance(endDates.get(columnIndex - 1), getCurrencyNode());\n                    }\n\n                    return getValue().getBalance(startDates.get(columnIndex - 1), endDates.get(columnIndex - 1),\n                            getCurrencyNode()).negate();\n                }\n\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/AccountBalanceChartController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.UUID;\nimport java.util.prefs.Preferences;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.BarChart;\nimport javafx.scene.chart.XYChart;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.report.ReportPeriod;\nimport jgnash.report.ReportPeriodUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.EncodeDecode;\nimport jgnash.util.Nullable;\n\n/**\n * Periodic Account Balance Bar Chart.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountBalanceChartController {\n\n    private static final String REPORT_PERIOD = \"reportPeriod\";\n\n    private static final String RUNNING_BALANCE = \"runningBalance\";\n\n    private static final String ENDING_BALANCE = \"endingBalance\";\n\n    private static final String SUB_ACCOUNTS = \"subAccounts\";\n\n    private static final String SELECTED_ACCOUNTS = \"selectedAccounts\";\n\n    private static final String INVERT_BALANCES = \"invertBalances\";\n\n    private static final int BAR_GAP = 1;\n\n    private static final int CATEGORY_GAP = 20;\n\n    private final Preferences preferences = Preferences.userNodeForPackage(AccountBalanceChartController.class)\n            .node(\"AccountBalanceChart\");\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private CheckBox invertBalanceCheckBox;\n\n    @FXML\n    private StackPane chartPane;\n\n    @FXML\n    private VBox accountComboVBox;\n\n    @FXML\n    private RadioButton endingBalanceRadioButton;\n\n    @FXML\n    private RadioButton runningBalanceRadioButton;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private ComboBox<ReportPeriod> periodComboBox;\n\n    @FXML\n    private BarChart<String, Number> barChart;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private CheckBox includeSubAccounts;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private CurrencyNode defaultCurrency;\n\n    private NumberFormat numberFormat;\n\n    private final Account NOP_ACCOUNT = new Account();\n\n    // List to retain auxiliary AccountComboBoxes\n    private final List<AccountComboBox> auxAccountComboBoxList = new ArrayList<>();\n\n    private final ChangeListener<Account> auxListener = (observable, oldValue, newValue) -> {\n        if (newValue != null) {\n            if (newValue == NOP_ACCOUNT) {\n                JavaFXUtils.runLater(AccountBalanceChartController.this::trimAuxAccountCombos);\n            } else {\n                if (!isEmptyAccountComboPresent()) {\n                    JavaFXUtils.runLater(() -> addAuxAccountCombo(null));\n                }\n            }\n\n            JavaFXUtils.runLater(AccountBalanceChartController.this::updateChart);\n            JavaFXUtils.runLater(AccountBalanceChartController.this::saveSelectedAccounts);\n        }\n    };\n\n    @FXML\n    public void initialize() {\n        accountComboBox.setPredicate(AccountComboBox.getShowAllPredicate());\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        periodComboBox.getItems().addAll(ReportPeriod.MONTHLY, ReportPeriod.QUARTERLY, ReportPeriod.YEARLY);\n        periodComboBox.setValue(ReportPeriod.values()[preferences.getInt(REPORT_PERIOD,\n                ReportPeriod.MONTHLY.ordinal())]);\n\n        defaultCurrency = engine.getDefaultCurrency();\n        numberFormat = NumericFormats.getFullCommodityFormat(defaultCurrency);\n\n        barChart.getYAxis().setLabel(defaultCurrency.getSymbol());\n        barChart.barGapProperty().set(BAR_GAP);\n        barChart.setCategoryGap(CATEGORY_GAP);\n        barChart.setLegendVisible(false);\n        barChart.getXAxis().setLabel(resources.getString(\"Column.Period\"));\n        barChart.getYAxis().setLabel(resources.getString(\"Column.Balance\") + \" : \" + defaultCurrency.getSymbol());\n\n        // Respect animation preference\n        barChart.animatedProperty().set(Options.animationsEnabledProperty().get());\n\n        startDatePicker.setValue(DateUtils.getFirstDayOfTheMonth(endDatePicker.getValue().minusMonths(12)));\n\n        // Force a defaults\n        includeSubAccounts.setSelected(preferences.getBoolean(SUB_ACCOUNTS, true));\n        runningBalanceRadioButton.setSelected(preferences.getBoolean(RUNNING_BALANCE, true));\n        endingBalanceRadioButton.setSelected(preferences.getBoolean(ENDING_BALANCE, false));\n        invertBalanceCheckBox.setSelected(preferences.getBoolean(INVERT_BALANCES, true));\n\n        restoreSelectedAccounts();\n\n        accountComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                defaultCurrency = newValue.getCurrencyNode();\n                numberFormat = NumericFormats.getFullCommodityFormat(defaultCurrency);\n\n                JavaFXUtils.runLater(AccountBalanceChartController.this::updateChart);\n                JavaFXUtils.runLater(AccountBalanceChartController.this::saveSelectedAccounts);\n            }\n        });\n\n        // Generic listener.  No super efficient but reduces listener count\n        final ChangeListener<Object> listener = (observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                JavaFXUtils.runLater(AccountBalanceChartController.this::updateChart);\n\n                preferences.putBoolean(ENDING_BALANCE, endingBalanceRadioButton.isSelected());\n                preferences.putBoolean(RUNNING_BALANCE, runningBalanceRadioButton.isSelected());\n                preferences.putBoolean(SUB_ACCOUNTS, includeSubAccounts.isSelected());\n                preferences.putInt(REPORT_PERIOD, periodComboBox.getValue().ordinal());\n                preferences.putBoolean(INVERT_BALANCES, invertBalanceCheckBox.isSelected());\n            }\n        };\n\n        addAuxAccountCombo(null);   // load the initial aux account combo\n\n        periodComboBox.valueProperty().addListener(listener);\n        startDatePicker.valueProperty().addListener(listener);\n        endDatePicker.valueProperty().addListener(listener);\n        runningBalanceRadioButton.selectedProperty().addListener(listener);\n        endingBalanceRadioButton.selectedProperty().addListener(listener);\n        includeSubAccounts.selectedProperty().addListener(listener);\n        invertBalanceCheckBox.selectedProperty().addListener(listener);\n\n        // Push the initial load to the end of the platform thread for better startup and a nicer visual effect\n        JavaFXUtils.runLater(this::updateChart);\n    }\n\n    /**\n     * Stores a list of selected accounts.\n     */\n    private void saveSelectedAccounts() {\n        final List<String> accounts = getSelectedAccounts().stream().map(account -> account.getUuid().toString())\n                .collect(Collectors.toList());\n\n        preferences.put(SELECTED_ACCOUNTS, EncodeDecode.encodeStringCollection(accounts));\n    }\n\n    private void restoreSelectedAccounts() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<String> accountIds\n                = new ArrayList<>(EncodeDecode.decodeStringCollection(preferences.get(SELECTED_ACCOUNTS, \"\")));\n\n        if (!accountIds.isEmpty()) {\n            // set Primary account\n            Account account = engine.getAccountByUuid(UUID.fromString(accountIds.get(0)));\n            if (account != null) {\n                accountComboBox.setValue(account);\n            }\n\n            if (accountIds.size() > 1) {\n                for (int i = 1; i < accountIds.size(); i++) {\n                    account = engine.getAccountByUuid(UUID.fromString(accountIds.get(i)));\n                    if (account != null) {\n                        addAuxAccountCombo(account);\n                    }\n                }\n            }\n        }\n\n        trimAuxAccountCombos();\n    }\n\n    private void addAuxAccountCombo(@Nullable Account account) {\n\n        final AccountComboBox auxComboBox = new AccountComboBox();\n        auxComboBox.setMaxWidth(Double.MAX_VALUE);\n        auxComboBox.setPredicate(AccountComboBox.getShowAllPredicate());\n        auxComboBox.getUnfilteredItems().add(0, NOP_ACCOUNT);\n        auxComboBox.setValue(account == null ? NOP_ACCOUNT : account);\n        auxComboBox.valueProperty().addListener(auxListener);\n\n        auxAccountComboBoxList.add(auxComboBox);\n        accountComboVBox.getChildren().add(auxComboBox);\n    }\n\n    private void trimAuxAccountCombos() {\n\n        final List<AccountComboBox> empty = auxAccountComboBoxList.stream()\n                .filter(accountComboBox -> accountComboBox.getValue() == NOP_ACCOUNT).collect(Collectors.toList());\n\n        // Reverse order so we leave the last empty at the bottom\n        Collections.reverse(empty);\n\n        // work backwards through the list to avoid use of an iterator, and leave at least one empty account combo\n        for (int i = empty.size() - 1; i > 0; i--) {\n            final AccountComboBox accountComboBox = empty.get(i);\n\n            accountComboVBox.getChildren().remove(accountComboBox);\n            auxAccountComboBoxList.remove(accountComboBox);\n            accountComboBox.valueProperty().removeListener(auxListener);\n        }\n    }\n\n    private boolean isEmptyAccountComboPresent() {\n        boolean result = false;\n\n        for (final AccountComboBox accountComboBox : auxAccountComboBoxList) {\n            if (accountComboBox.getValue() == NOP_ACCOUNT) {\n                result = true;\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    private Collection<Account> getSelectedAccounts() {\n        // Use a list for consistent sort order\n        final List<Account> accountList = new ArrayList<>();\n        accountList.add(accountComboBox.getValue());\n\n        for (final AccountComboBox auxAccountComboBox : auxAccountComboBoxList) {\n            final Account account = auxAccountComboBox.getValue();\n            if (account != NOP_ACCOUNT && !accountList.contains(account)) {\n                accountList.add(account);\n            }\n        }\n\n        return accountList;\n    }\n\n    private void updateChart() {\n        barChart.getData().clear();\n\n        final List<ReportPeriodUtils.Descriptor> descriptors = ReportPeriodUtils.getDescriptors(\n                periodComboBox.getValue(), startDatePicker.getValue(), endDatePicker.getValue());\n\n        // Create a set of accounts to display\n        final Collection<Account> selectedAccounts = getSelectedAccounts();\n\n        barChart.setLegendVisible(selectedAccounts.size() > 1);\n\n        for (final Account account : selectedAccounts) {\n\n            final XYChart.Series<String, Number> series = new XYChart.Series<>();\n            series.setName(account.getName());\n            barChart.getData().add(series);\n\n            for (final ReportPeriodUtils.Descriptor descriptor : descriptors) {\n                final BigDecimal income;\n\n                if (!includeSubAccounts.isSelected()) {\n\n                    if (runningBalanceRadioButton.isSelected()) {\n                        income = account.getBalance(descriptor.getEndDate());\n                    } else {    // ending balance\n                        income = account.getBalance(descriptor.getStartDate(), descriptor.getEndDate());\n                    }\n                } else {\n                    if (runningBalanceRadioButton.isSelected()) {\n                        income = account.getTreeBalance(descriptor.getEndDate(), account.getCurrencyNode());\n                    } else {    // ending balance\n                        income = account.getTreeBalance(descriptor.getStartDate(), descriptor.getEndDate(),\n                                account.getCurrencyNode());\n                    }\n                }\n\n                series.getData().add(new XYChart.Data<>(descriptor.getLabel(),\n                        invertBalance(income, account.getAccountType())));\n            }\n\n            for (final XYChart.Data<String, Number> data : series.getData()) {\n                Tooltip.install(data.getNode(), new Tooltip(numberFormat.format(data.getYValue())));\n            }\n        }\n    }\n\n    /**\n     * Inverts the account balance based on account type\n     * @param amount account balance to invert\n     * @param type the account type\n     * @return the corrected account balance\n     */\n    private BigDecimal invertBalance(final BigDecimal amount, final AccountType type) {\n        if (invertBalanceCheckBox.isSelected()) {\n            if (type == AccountType.INCOME || type == AccountType.EQUITY || type == AccountType.CREDIT\n                    || type == AccountType.LIABILITY) {\n\n                return amount.negate();\n            }\n        }\n\n        return amount;\n    }\n\n    @FXML\n    private void handleSaveAction() {\n        ChartUtilities.saveChart(chartPane);\n    }\n\n    @FXML\n    private void handleCopyToClipboard() {\n        ChartUtilities.copyToClipboard(chartPane);\n    }\n\n    @FXML\n    private void handlePrintAction() {\n        ChartUtilities.printChart(chartPane);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/AccountRegisterReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.ResourceBundle;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.FilteredList;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionType;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.report.table.Row;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.views.register.RegisterFactory;\nimport jgnash.util.Nullable;\n\n/**\n * Account Register Report Model\n *\n * @author Craig Cavanaugh\n */\npublic class AccountRegisterReport extends Report {\n\n    AccountRegisterReport() {\n        setForceGroupPagination(false);\n    }\n\n    public static AbstractReportTableModel createReportModel(final Account account, final LocalDate startDate,\n                                                      final LocalDate endDate, final boolean showSplits,\n                                                      final String memoFilter, final String payeeFilter,\n                                                      final boolean showTimeStamp) {\n\n        if (account.getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n            return new AccountRegisterReport.InvestmentAccountReportModel(account, startDate, endDate, memoFilter,\n                    showTimeStamp);\n        }\n\n        return new AccountRegisterReport.AccountReportModel(account, showSplits, startDate, endDate, memoFilter,\n                payeeFilter, showTimeStamp);\n\n    }\n\n    private static class AccountReportModel extends AbstractReportTableModel {\n\n        private static final String INDENT_PREFIX = \"  - \";\n\n        private static final String SPLIT = ResourceUtils.getString(\"Button.Splits\");\n\n        private final boolean showSplits;\n\n        private final boolean sumAmounts;\n\n        private final Account account;\n\n        private final ObservableList<Row<Transaction>> transactionRows = FXCollections.observableArrayList();\n\n        private final FilteredList<Row<Transaction>> filteredList = new FilteredList<>(transactionRows);\n\n        private String[] columnNames = RegisterFactory.getColumnNames(AccountType.BANK);\n\n        private final boolean showTimestamp;\n\n        private static final ColumnStyle[] columnStyles = new ColumnStyle[]{ColumnStyle.SHORT_DATE, ColumnStyle.TIMESTAMP,\n                ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING,\n                ColumnStyle.SHORT_AMOUNT, ColumnStyle.SHORT_AMOUNT, ColumnStyle.BALANCE};\n\n        AccountReportModel(@Nullable final Account account, final boolean showSplits, final LocalDate startDate,\n                           final LocalDate endDate, final String memoFilter, final String payeeFilter,\n                           final boolean showTimestamp) {\n            this.account = account;\n            this.showSplits = showSplits;\n\n            sumAmounts = (memoFilter != null && !memoFilter.isEmpty())\n                    || (payeeFilter != null && !payeeFilter.isEmpty());\n\n            filteredList.setPredicate(new TransactionAfterDatePredicate(startDate)\n                    .and(new TransactionBeforeDatePredicate(endDate))\n                    .and(new MemoPredicate(memoFilter))\n                    .and(new PayeePredicate(payeeFilter)));\n\n            this.showTimestamp = showTimestamp;\n\n            loadAccount();\n        }\n\n        @Override\n        public String getTitle() {\n            return account.getName();\n        }\n\n        @Override\n        public String getSubTitle() {\n            return null;\n        }\n\n        @Override\n        public String getGroupFooterLabel() {\n            return rb.getString(\"Word.Totals\");\n        }\n\n        @Override\n        public int[] getColumnsToHide() {\n\n            if (!showTimestamp && sumAmounts) {\n                return new int[]{1, 9};\n            } else if (!showTimestamp) {\n                return new int[]{1};\n            }\n\n            return super.getColumnsToHide();\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            switch (columnIndex) {\n                case 0:\n                case 1:\n                case 2:\n                case 6:\n                case 7:\n                case 8:\n                case 9:\n                    return true;\n                default:\n                    return false;\n            }\n        }\n\n        public float getColumnWidthWeight(int columnIndex) {\n            switch (columnIndex) {\n                case 3:\n                case 5:\n                    return 30;\n                case 4:\n                    return 40;\n                default:\n                    return 0;\n            }\n        }\n\n        private void loadAccount() {\n            if (account != null) {\n                if (sumAmounts) {   // dump the running total column as it does not make sense when filtering\n                    final String[] base = RegisterFactory.getColumnNames(account.getAccountType());\n                    columnNames = Arrays.copyOfRange(base, 0, base.length - 1);\n                } else {\n                    columnNames = RegisterFactory.getColumnNames(account.getAccountType());\n                }\n\n                for (final Transaction transaction : account.getSortedTransactionList()) {\n                    if (showSplits && transaction.getTransactionType() == TransactionType.SPLITENTRY\n                            && transaction.getCommonAccount() == account) {\n                        transactionRows.add(new TransactionRow(transaction, -1));\n                        List<TransactionEntry> transactionEntries = transaction.getTransactionEntries();\n                        for (int i = 0; i < transactionEntries.size(); i++) {\n                            transactionRows.add(new TransactionRow(transaction, i));\n                        }\n                    } else {\n                        transactionRows.add(new TransactionRow(transaction, -1));\n                    }\n                }\n            }\n        }\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return account.getCurrencyNode();\n        }\n\n        @Override\n        public ColumnStyle getColumnStyle(final int columnIndex) {\n\n            // Override defaults is summing the accounts\n            if (sumAmounts && columnIndex == columnNames.length) {\n                return ColumnStyle.GROUP_NO_HEADER;\n            } else if (columnStyles[columnIndex] == ColumnStyle.SHORT_AMOUNT && sumAmounts) {\n                return ColumnStyle.AMOUNT_SUM;\n            }\n\n            return columnStyles[columnIndex];\n        }\n\n        @Override\n        public int getRowCount() {\n            return filteredList.size();\n        }\n\n        @Override\n        public int getColumnCount() {\n            if (sumAmounts) {\n                return columnNames.length + 1;\n            }\n            return columnNames.length;\n        }\n\n        @Override\n        public String getColumnName(final int columnIndex) {\n            if (sumAmounts && columnIndex == columnNames.length) {\n                return \"group\";\n            }\n            return columnNames[columnIndex];\n        }\n\n        @Override\n        public Class<?> getColumnClass(final int columnIndex) {\n\n            if (sumAmounts && columnIndex == columnNames.length) { // group column\n                return String.class;\n            }\n\n            switch (columnIndex) {\n                case 0:\n                    return LocalDate.class;\n                case 1:\n                    return LocalDateTime.class;\n                case 2:\n                case 3:\n                case 4:\n                case 5:\n                case 6:\n                    return String.class;\n                default:\n                    return BigDecimal.class;\n            }\n        }\n\n        @Override\n        public Object getValueAt(final int rowIndex, final int columnIndex) {\n            return filteredList.get(rowIndex).getValueAt(columnIndex);\n        }\n\n        private class TransactionRow extends Row<Transaction> {\n            private final BigDecimal amount;\n            private final int signum;\n            private final TransactionEntry transactionEntry;\n\n            TransactionRow(final Transaction transaction, final int entry) {\n                super(transaction);\n\n                if (entry >= 0) {\n                    transactionEntry = transaction.getTransactionEntries().get(entry);\n                    amount = transactionEntry.getAmount(account);\n                } else {\n                    transactionEntry = null;\n                    amount = transaction.getAmount(account);\n                }\n\n                signum = amount.signum();\n            }\n\n            @Override\n            public Object getValueAt(final int columnIndex) {\n\n                if (sumAmounts && columnIndex == columnNames.length) {\n                    return \"group\";\n                }\n\n                final Transaction transaction = getValue();\n\n                if (transactionEntry == null) {\n                    switch (columnIndex) {\n                        case 0:\n                            return transaction.getLocalDate();\n                        case 1:\n                            return transaction.getTimestamp();\n                        case 2:\n                            return transaction.getNumber();\n                        case 3:\n                            return transaction.getPayee();\n                        case 4:\n                            return transaction.getMemo(account);\n                        case 5:\n                            final int count = transaction.size();\n\n                            if (count > 1) {\n                                return \"[ \" + count + \" \" + SPLIT + \" ]\";\n                            }\n\n                            final TransactionEntry entry = transaction.getTransactionEntries().get(0);\n\n                            if (entry.getCreditAccount() != account) {\n                                return entry.getCreditAccount().getName();\n                            }\n                            return entry.getDebitAccount().getName();\n                        case 6:\n                            return transaction.getReconciled(account) != ReconciledState.NOT_RECONCILED\n                                    ? transaction.getReconciled(account).toString() : null;\n                        case 7:\n                            if (signum >= 0) {\n                                return amount;\n                            }\n                            return null;\n                        case 8:\n                            if (signum < 0) {\n                                return amount.abs();\n                            }\n                            return null;\n                        case 9:\n                            return account.getBalanceAt(transaction);\n                        default:\n                            return null;\n                    }\n                }\n\n                switch (columnIndex) {\n                    case 4:\n                        return transactionEntry.getMemo();\n                    case 5:\n                        if (transactionEntry.getCreditAccount() != account) {\n                            return INDENT_PREFIX + transactionEntry.getCreditAccount().getName();\n                        }\n                        return INDENT_PREFIX + transactionEntry.getDebitAccount().getName();\n                    case 6:\n                        return transaction.getReconciled(account) != ReconciledState.NOT_RECONCILED\n                                ? transaction.getReconciled(account).toString() : null;\n                    case 7:\n                        if (signum >= 0) {\n                            return amount;\n                        }\n                        return null;\n                    case 8:\n                        if (signum < 0) {\n                            return amount.abs();\n                        }\n                        return null;\n                    default:\n                        return null;\n                }\n            }\n        }\n    }\n\n    private static class InvestmentAccountReportModel extends AbstractReportTableModel {\n\n        private final ResourceBundle resources = ResourceUtils.getBundle();\n\n        private final Account account;\n\n        private final ObservableList<Row<Transaction>> transactionRows = FXCollections.observableArrayList();\n\n        private final FilteredList<Row<Transaction>> filteredList = new FilteredList<>(transactionRows);\n\n        private String[] columnNames = RegisterFactory.getColumnNames(AccountType.INVEST);\n\n        private static final ColumnStyle[] columnStyles = new ColumnStyle[]{ColumnStyle.SHORT_DATE, ColumnStyle.TIMESTAMP,\n                ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.SHORT_AMOUNT,\n                ColumnStyle.SHORT_AMOUNT, ColumnStyle.AMOUNT_SUM};\n\n        private final boolean showTimestamp;\n\n        InvestmentAccountReportModel(@Nullable final Account account, final LocalDate startDate,\n                                     final LocalDate endDate, final String memoFilter, final boolean showTimestamp) {\n            this.account = account;\n\n            filteredList.setPredicate(new TransactionAfterDatePredicate(startDate)\n                    .and(new TransactionBeforeDatePredicate(endDate))\n                    .and(new MemoPredicate(memoFilter)));\n\n            this.showTimestamp = showTimestamp;\n\n            loadAccount();\n        }\n\n        @Override\n        public String getTitle() {\n            return account.getName();\n        }\n\n        @Override\n        public String getSubTitle() {\n            return null;\n        }\n\n        @Override\n        public int[] getColumnsToHide() {\n            if (showTimestamp) {\n                return super.getColumnsToHide();\n            }\n\n            return new int[]{1};\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            switch (columnIndex) {\n                case 0:\n                case 1:\n                case 4:\n                case 5:\n                case 6:\n                case 7:\n                case 8:\n                    return true;\n                default:\n                    return false;\n            }\n        }\n\n        private void loadAccount() {\n            if (account != null) {\n                columnNames = RegisterFactory.getColumnNames(account.getAccountType());\n\n                transactionRows.addAll(account.getSortedTransactionList().stream()\n                        .map(TransactionRow::new).collect(Collectors.toList()));\n            }\n        }\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return account.getCurrencyNode();\n        }\n\n        @Override\n        public ColumnStyle getColumnStyle(final int columnIndex) {\n            return columnStyles[columnIndex];\n        }\n\n        @Override\n        public int getRowCount() {\n            return filteredList.size();\n        }\n\n        @Override\n        public int getColumnCount() {\n            return columnNames.length;\n        }\n\n        @Override\n        public String getColumnName(final int columnIndex) {\n            return columnNames[columnIndex];\n        }\n\n        @Override\n        public Class<?> getColumnClass(final int columnIndex) {\n            switch (columnIndex) {\n                case 0:\n                    return LocalDate.class;\n                case 1:\n                    return LocalDateTime.class;\n                case 2:\n                case 3:\n                case 4:\n                case 5:\n                    return String.class;\n                default:\n                    return BigDecimal.class;\n            }\n        }\n\n        @Override\n        public Object getValueAt(final int rowIndex, final int columnIndex) {\n            return filteredList.get(rowIndex).getValueAt(columnIndex);\n        }\n\n        private class TransactionRow extends Row<Transaction> {\n\n            TransactionRow(final Transaction transaction) {\n                super(transaction);\n            }\n\n            @Override\n            public Object getValueAt(final int columnIndex) {\n                final Transaction transaction = getValue();\n\n                switch (columnIndex) {\n                    case 0:\n                        return transaction.getLocalDate();\n                    case 1:\n                        return transaction.getTimestamp();\n                    case 2:\n                        if (transaction instanceof InvestmentTransaction) {\n                            return transaction.getTransactionType().toString();\n                        } else if (transaction.getAmount(account).signum() > 0) {\n                            return resources.getString(\"Item.CashDeposit\");\n                        } else {\n                            return resources.getString(\"Item.CashWithdrawal\");\n                        }\n                    case 3:\n                        if (transaction instanceof InvestmentTransaction) {\n                            return ((InvestmentTransaction) transaction).getSecurityNode().getSymbol();\n                        }\n                        return null;\n                    case 4:\n                        return transaction.getMemo(account);\n                    case 5:\n                        return transaction.getReconciled(account) != ReconciledState.NOT_RECONCILED\n                                ? transaction.getReconciled(account).toString() : null;\n                    case 6:\n                        if (transaction instanceof InvestmentTransaction) {\n                            return ((InvestmentTransaction) transaction).getQuantity();\n                        }\n                        return null;\n                    case 7:\n                        if (transaction instanceof InvestmentTransaction) {\n                            return ((InvestmentTransaction) transaction).getPrice();\n                        }\n                        return null;\n                    case 8:\n                        if (transaction instanceof InvestmentTransaction) {\n                            return ((InvestmentTransaction) transaction).getNetCashValue();\n                        }\n                        return transaction.getAmount(account);\n                    default:\n                        return null;\n                }\n            }\n        }\n    }\n\n    private static class TransactionBeforeDatePredicate implements Predicate<Row<Transaction>> {\n\n        private final LocalDate localDate;\n\n        TransactionBeforeDatePredicate(final LocalDate localDate) {\n            this.localDate = localDate;\n        }\n\n        @Override\n        public boolean test(final Row<Transaction> transactionRow) {\n            return DateUtils.before(transactionRow.getValue().getLocalDate(), localDate);\n        }\n    }\n\n    private static class TransactionAfterDatePredicate implements Predicate<Row<Transaction>> {\n\n        private final LocalDate localDate;\n\n        TransactionAfterDatePredicate(final LocalDate localDate) {\n            this.localDate = localDate;\n        }\n\n        @Override\n        public boolean test(final Row<Transaction> transactionRow) {\n            return DateUtils.after(transactionRow.getValue().getLocalDate(), localDate);\n        }\n    }\n\n    private static class MemoPredicate implements Predicate<Row<Transaction>> {\n\n        private final String filter;\n\n        MemoPredicate(final String memo) {\n            if (memo != null && !memo.isEmpty()) {\n                filter = memo.toLowerCase(Locale.getDefault());\n            } else {\n                filter = memo;\n            }\n        }\n\n        @Override\n        public boolean test(final Row<Transaction> transactionRow) {\n            return transactionRow.getValue().getMemo().toLowerCase(Locale.getDefault()).contains(filter);\n        }\n    }\n\n    private static class PayeePredicate implements Predicate<Row<Transaction>> {\n\n        private final String filter;\n\n        PayeePredicate(final String payee) {\n            if (payee != null && !payee.isEmpty()) {\n                filter = payee.toLowerCase(Locale.getDefault());\n            } else {\n                filter = payee;\n            }\n        }\n\n        @Override\n        public boolean test(final Row<Transaction> transactionRow) {\n            return transactionRow.getValue().getPayee().toLowerCase(Locale.getDefault()).contains(filter);\n        }\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/AccountRegisterReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.TextField;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.report.pdf.ReportController;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.Nullable;\n\n/**\n * Account Register Report Controller\n *\n * @author Craig Cavanaugh\n */\npublic class AccountRegisterReportController implements ReportController {\n\n    @FXML\n    private TextField memoFilterTextField;\n\n    @FXML\n    private TextField payeeFilterTextField;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private CheckBox showSplitsCheckBox;\n\n    @FXML\n    private CheckBox showTimestampCheckBox;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    private static final String SHOW_SPLITS = \"showSplits\";\n\n    private static final String SHOW_TIMESTAMP = \"showTimestamp\";\n\n    private final Report report = new AccountRegisterReport();\n\n    private Runnable refreshRunnable = null;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Object> changeListener;  // need to hold a reference to prevent premature collection\n\n    public AccountRegisterReportController() {\n        super();\n    }\n\n    @FXML\n    private void initialize() {\n        final Preferences preferences = getPreferences();\n\n        showSplitsCheckBox.setSelected(preferences.getBoolean(SHOW_SPLITS, false));\n        showTimestampCheckBox.setSelected(preferences.getBoolean(SHOW_TIMESTAMP, false));\n\n        startDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        startDatePicker.preferencesProperty().setValue(preferences);\n        startDatePicker.preferenceKeyProperty().setValue(START_DATE_KEY);\n\n        endDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        endDatePicker.preferencesProperty().setValue(preferences);\n        endDatePicker.preferenceKeyProperty().setValue(END_DATE_KEY);\n\n        accountComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                refreshAccount(newValue);\n                handleReportRefresh();\n            }\n        });\n\n        changeListener = (observable, oldValue, newValue) -> handleReportRefresh();\n\n        showSplitsCheckBox.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n        startDatePicker.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n        endDatePicker.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n        payeeFilterTextField.textProperty().addListener(new WeakChangeListener<>(changeListener));\n        memoFilterTextField.textProperty().addListener(new WeakChangeListener<>(changeListener));\n        showTimestampCheckBox.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n    }\n\n    @Override\n    public void setRefreshRunnable(final Runnable runnable) {\n        refreshRunnable = runnable;\n    }\n\n    @Override\n    public void getReport(final Consumer<Report> reportConsumer) {\n        reportConsumer.accept(report);\n    }\n\n    @Override\n    public void refreshReport() {\n        handleReportRefresh();\n    }\n\n    public void setAccount(@Nullable final Account account) {\n        if (account != null) {\n            accountComboBox.setValue(account);\n        } else {    // load the selected account\n            refreshAccount(accountComboBox.getValue());\n        }\n    }\n\n    private void refreshAccount(final Account account) {\n        if (account != null) {\n            if (!Options.restoreReportDateProperty().get()) {\n                resetDates();\n            }\n        }\n    }\n\n    private void resetDates() {\n        if (accountComboBox.getValue() != null && accountComboBox.getValue().getTransactionCount() > 0 ) {\n            startDatePicker.setValue(accountComboBox.getValue().getTransactionAt(0).getLocalDate());\n            endDatePicker.setValue(LocalDate.now());\n        }\n    }\n\n    private void handleReportRefresh() {\n\n        final Preferences preferences = getPreferences();\n\n        preferences.putBoolean(SHOW_SPLITS, showSplitsCheckBox.isSelected());\n        preferences.putBoolean(SHOW_TIMESTAMP, showTimestampCheckBox.isSelected());\n\n        if (accountComboBox.getValue() != null) {\n            addTable();\n\n            // send notification the report has been updated\n            if (refreshRunnable != null) {\n                refreshRunnable.run();\n            }\n        }\n    }\n\n    private void addTable() {\n        final AbstractReportTableModel model = createReportModel();\n\n        report.clearReport();\n\n        try {\n            report.addTable(model);\n            report.addFooter();\n        } catch (final IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public AbstractReportTableModel createReportModel() {\n        final Account account = accountComboBox.getValue();\n\n        // disable the payee filter if an investment account is selected\n        payeeFilterTextField.setDisable(account.getAccountType().getAccountGroup() == AccountGroup.INVEST);\n        showSplitsCheckBox.setDisable(account.getAccountType().getAccountGroup() == AccountGroup.INVEST);\n\n        return AccountRegisterReport.createReportModel(account, startDatePicker.getValue(), endDatePicker.getValue(),\n                showSplitsCheckBox.isSelected(), memoFilterTextField.getText(), payeeFilterTextField.getText(),\n                showTimestampCheckBox.isSelected());\n    }\n\n    @FXML\n    private void handleResetAll() {\n        JavaFXUtils.runLater(() -> {\n            resetDates();\n            memoFilterTextField.setText(\"\");\n            payeeFilterTextField.setText(\"\");\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/BalanceByMonthOptionsDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.time.LocalDate;\nimport java.util.Optional;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.RadioButton;\nimport javafx.stage.Stage;\n\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Simple date range input controller.\n *\n * @author Craig Cavanaugh\n */\npublic class BalanceByMonthOptionsDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private CheckBox defaultCurrencyCheckBox;\n\n    @FXML\n    private RadioButton verticalRadioButton;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    private LocalDate[] dates = null;\n\n    private final BooleanProperty forceCurrency = new SimpleBooleanProperty();\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        startDatePicker.setValue(endDatePicker.getValue().minusYears(1));\n\n        defaultCurrencyCheckBox.selectedProperty().bindBidirectional(forceCurrency);\n    }\n\n    Optional<LocalDate[]> getDates() {\n        return Optional.ofNullable(dates);\n    }\n\n    boolean isVertical() {\n        return verticalRadioButton.isSelected();\n    }\n\n    BooleanProperty forceDefaultCurrencyProperty() {\n        return forceCurrency;\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleOkAction() {\n        dates = new LocalDate[] {startDatePicker.getValue(), endDatePicker.getValue()};\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/BalanceSheetReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.report.table.Row;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Balance Sheet Report\n *\n * @author Craig Cavanaugh\n */\npublic class BalanceSheetReport extends AbstractSumByTypeReport {\n\n    BalanceSheetReport() {\n        super();\n\n        setRunningTotal(false);\n        setAddCrossTabColumn(false);\n        setForceGroupPagination(false);\n    }\n\n    @Override\n    protected ReportModel createReportModel(final LocalDate startDate, final LocalDate endDate, final boolean hideZeroBalanceAccounts) {\n        ReportModel model = super.createReportModel(startDate, endDate, hideZeroBalanceAccounts);\n\n\n        // load retained profit and loss row\n        model.addRow(new RetainedEarningsRow());\n\n        return model;\n    }\n\n    @Override\n    protected List<AccountGroup> getAccountGroups() {\n        List<AccountGroup> groups = new ArrayList<>();\n\n        groups.add(AccountGroup.ASSET);\n        groups.add(AccountGroup.INVEST);\n        groups.add(AccountGroup.SIMPLEINVEST);\n        groups.add(AccountGroup.LIABILITY);\n        groups.add(AccountGroup.EQUITY);\n\n        return groups;\n    }\n\n    @Override\n    public String getGrandTotalLegend() {\n        return rb.getString(\"Word.Difference\");\n    }\n\n    @Override\n    public String getGroupFooterLabel() {\n        return rb.getString(\"Word.Subtotal\");\n    }\n\n    /**\n     * Internal class to return a row the calculates the retained earnings for an account.\n     */\n    private class RetainedEarningsRow extends Row<Void> {\n\n        RetainedEarningsRow() {\n            super(null);\n        }\n\n        /**\n         * Returns values for retained earnings.\n         */\n        @Override\n        public Object getValueAt(final int columnIndex) {\n\n            if (columnIndex == 0) { // account column\n                return rb.getString(\"Title.RetainedEarnings\");\n            } else if (columnIndex == getColumnCount() - 1) { // group column\n                return AccountGroup.EQUITY.toString();\n            } else if (columnIndex > 0 && columnIndex <= startDates.size()) {\n                return getRetainedProfitLoss(startDates.get(columnIndex - 1), endDates.get(columnIndex - 1));\n            }\n\n            return null;\n        }\n\n        private int getColumnCount() {\n            return startDates.size() + 2;\n        }\n\n        /**\n         * Returns the retained profit or loss for the given period.\n         *\n         * @param startDate Start date for the period\n         * @param endDate End date for the period\n         * @return the profit or loss for the period\n         */\n        private BigDecimal getRetainedProfitLoss(final LocalDate startDate, final LocalDate endDate) {\n            BigDecimal profitLoss = BigDecimal.ZERO;\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            final CurrencyNode baseCurrency = engine.getDefaultCurrency();\n\n            for (final Account account : engine.getExpenseAccountList()) {\n                profitLoss = profitLoss.add(account.getBalance(startDate, endDate, baseCurrency));\n            }\n\n            for (final Account account : engine.getIncomeAccountList()) {\n                profitLoss = profitLoss.add(account.getBalance(startDate, endDate, baseCurrency));\n            }\n\n            return profitLoss.negate();\n        }\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/BalanceSheetReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.time.Period;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.report.pdf.ReportController;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Balance Sheet Report Controller\n *\n * @author Craig Cavanaugh\n */\npublic class BalanceSheetReportController implements ReportController {\n\n    @FXML\n    private ComboBox<Period> resolutionComboBox;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private CheckBox hideZeroBalanceAccounts;\n\n    private static final String HIDE_ZERO_BALANCE = \"hideZeroBalance\";\n\n    private static final String PERIOD = \"period\";\n\n    private static final String MONTHS = \"months\";\n\n    private final BalanceSheetReport report = new BalanceSheetReport();\n\n    private Runnable refreshRunnable = null;\n\n    private final Preferences preferences = getPreferences();\n\n    public BalanceSheetReportController() {\n        super();\n    }\n\n    @FXML\n    private void initialize() {\n        final Preferences preferences = getPreferences();\n\n        hideZeroBalanceAccounts.setSelected(preferences.getBoolean(HIDE_ZERO_BALANCE, true));\n\n        resolutionComboBox.getItems().setAll(Period.MONTHLY, Period.QUARTERLY, Period.YEARLY);\n        resolutionComboBox.setValue(Period.values()[preferences.getInt(PERIOD, Period.MONTHLY.ordinal())]);\n\n        resetDates();\n\n        startDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        startDatePicker.preferencesProperty().setValue(preferences);\n        startDatePicker.preferenceKeyProperty().setValue(START_DATE_KEY);\n\n        endDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        endDatePicker.preferencesProperty().setValue(preferences);\n        endDatePicker.preferenceKeyProperty().setValue(END_DATE_KEY);\n\n        startDatePicker.valueProperty().addListener((observable, oldValue, newValue) -> handleReportRefresh());\n        endDatePicker.valueProperty().addListener((observable, oldValue, newValue) -> handleReportRefresh());\n        hideZeroBalanceAccounts.onActionProperty().setValue(event -> handleReportRefresh());\n        resolutionComboBox.valueProperty().addListener((observable, oldValue, newValue) -> handleReportRefresh());\n\n        // boot the report generation\n        JavaFXUtils.runLater(this::refreshReport);\n    }\n\n    @Override\n    public void setRefreshRunnable(final Runnable runnable) {\n        refreshRunnable = runnable;\n    }\n\n    @Override\n    public void getReport(final Consumer<Report> reportConsumer) {\n        reportConsumer.accept(report);\n    }\n\n    @Override\n    public void refreshReport() {\n        handleReportRefresh();\n    }\n\n    private void handleReportRefresh() {\n\n        preferences.putBoolean(HIDE_ZERO_BALANCE, hideZeroBalanceAccounts.isSelected());\n        preferences.putInt(MONTHS, DateUtils.getLastDayOfTheMonths(startDatePicker.getValue(),\n                endDatePicker.getValue()).size());\n        preferences.putInt(PERIOD, resolutionComboBox.getValue().ordinal());\n\n        addTable();\n\n        // send notification the report has been updated\n        if (refreshRunnable != null) {\n            refreshRunnable.run();\n        }\n    }\n\n    private void resetDates() {\n\n        // calculate number of months for 4-5 columns\n        final int months = (int)(resolutionComboBox.getValue().getMonths() * 4);\n\n        endDatePicker.setValue(LocalDate.now());\n        startDatePicker.setValue(DateUtils.getFirstDayOfTheMonth(LocalDate.now().minusMonths(months - 1)));\n    }\n\n    private void addTable() {\n        final AbstractReportTableModel model = createReportModel();\n\n        report.clearReport();\n        report.setTitle(ResourceUtils.getString(\"Title.BalanceSheet\"));\n\n        try {\n            report.addTable(model);\n            report.addFooter();\n        } catch (final IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public AbstractReportTableModel createReportModel() {\n        report.setReportPeriod(resolutionComboBox.getValue());\n\n        return report.createReportModel(startDatePicker.getValue(), endDatePicker.getValue(),\n                hideZeroBalanceAccounts.isSelected());\n    }\n\n    @FXML\n    private void handleResetAll() {\n        resetDates();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/ChartUtilities.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport javax.imageio.ImageIO;\n\nimport javafx.embed.swing.SwingFXUtils;\nimport javafx.scene.SnapshotParameters;\nimport javafx.scene.chart.Chart;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.image.WritableImage;\nimport javafx.scene.input.Clipboard;\nimport javafx.scene.input.ClipboardContent;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.transform.Scale;\nimport javafx.stage.FileChooser;\n\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.FileUtils;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * JavaFX Chart utilities.\n *\n * @author Craig Cavanaugh\n */\nclass ChartUtilities {\n\n    private ChartUtilities() {\n        // Utility class\n    }\n\n    /**\n     * Increases scale/resolution of captured image.  This could be made user configurable\n     */\n    private static final int SNAPSHOT_SCALE_FACTOR = 4;\n\n    private static WritableImage takeSnapshot(final Pane pane) {\n\n        Map<Chart, Boolean> animationMap = new HashMap<>();\n\n        // Need to disable chart animations for printing\n        pane.getChildren().stream().filter(node -> node instanceof Chart).forEach(node -> {\n            animationMap.put((Chart) node, ((Chart) node).getAnimated());\n\n            // Need to disable animation for printing\n            ((Chart) node).setAnimated(false);\n        });\n\n        final SnapshotParameters snapshotParameters = new SnapshotParameters();\n        snapshotParameters.setTransform(new Scale(SNAPSHOT_SCALE_FACTOR, SNAPSHOT_SCALE_FACTOR));\n\n        final WritableImage image = pane.snapshot(snapshotParameters, null);\n\n        // Restore animation\n        for (Map.Entry<Chart, Boolean> entry : animationMap.entrySet()) {\n            entry.getKey().setAnimated(entry.getValue());\n        }\n\n        return image;\n    }\n\n    static void saveChart(final Pane pane) {\n        final FileChooser fileChooser = new FileChooser();\n        fileChooser.setTitle(ResourceUtils.getString(\"Title.SaveFile\"));\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"PNG\", \"*.png\")\n        );\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            final WritableImage image = takeSnapshot(pane);\n\n            try {\n                final String type = FileUtils.getFileExtension(file.toString().toLowerCase(Locale.ROOT));\n\n                ImageIO.write(SwingFXUtils.fromFXImage(image, null), type, file);\n            } catch (final IOException e) {\n                StaticUIMethods.displayException(e);\n            }\n        }\n    }\n\n    static void copyToClipboard(final Pane pane) {\n        final Clipboard clipboard = Clipboard.getSystemClipboard();\n        final ClipboardContent content = new ClipboardContent();\n\n        content.putImage(takeSnapshot(pane));\n\n        clipboard.setContent(content);\n    }\n\n    static void printChart(final Pane pane) {\n        // Manipulate a snapshot of the pane instead of the pane itself to avoid visual artifacts when scaling\n        JavaFXUtils.printImageView(new ImageView(takeSnapshot(pane)));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/IncomeExpenseBarChartDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.BarChart;\nimport javafx.scene.chart.XYChart;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.report.ReportPeriod;\nimport jgnash.report.ReportPeriodUtils;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.resource.cursor.CustomCursor;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Income and Expense Bar Chart.\n *\n * @author Craig Cavanaugh\n * @author Pranay Kumar\n */\npublic class IncomeExpenseBarChartDialogController {\n\n    private static final String REPORT_PERIOD = \"reportPeriod\";\n\n    private static final int BAR_GAP = 1;\n\n    private static final int PERIOD_GAP = 20;\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private StackPane chartPane;\n\n    @FXML\n    private ComboBox<ReportPeriod> periodComboBox;\n\n    @FXML\n    private BarChart<String, Number> barChart;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private CurrencyNode defaultCurrency;\n\n    private NumberFormat numberFormat;\n\n    @FXML\n    public void initialize() {\n\n        final Preferences preferences = Preferences.userNodeForPackage(IncomeExpenseBarChartDialogController.class)\n                .node(\"IncomeExpenseBarChart\");\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        periodComboBox.getItems().addAll(ReportPeriod.values());\n        periodComboBox.setValue(ReportPeriod.values()[preferences.getInt(REPORT_PERIOD, ReportPeriod.MONTHLY.ordinal())]);\n\n        defaultCurrency = engine.getDefaultCurrency();\n        numberFormat = NumericFormats.getFullCommodityFormat(defaultCurrency);\n\n        barChart.getYAxis().setLabel(defaultCurrency.getSymbol());\n        barChart.barGapProperty().set(BAR_GAP);\n        barChart.setCategoryGap(PERIOD_GAP);\n\n        // Respect animation preference\n        barChart.animatedProperty().set(Options.animationsEnabledProperty().get());\n\n        startDatePicker.setValue(DateUtils.getFirstDayOfTheMonth(endDatePicker.getValue().minusMonths(11)));\n\n        final ChangeListener<Object> listener = (observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                JavaFXUtils.runLater(this::updateChart);\n            }\n        };\n\n        startDatePicker.valueProperty().addListener(listener);\n        endDatePicker.valueProperty().addListener(listener);\n\n        periodComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            preferences.putInt(REPORT_PERIOD, newValue.ordinal());\n            JavaFXUtils.runLater(this::updateChart);\n        });\n\n        // Push the initial load to the end of the platform thread for better startup and a nicer visual effect\n        JavaFXUtils.runLater(this::updateChart);\n    }\n\n    private void updateChart() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<Account> incomeAccounts = engine.getIncomeAccountList();\n\n        final List<Account> expenseAccounts = engine.getExpenseAccountList();\n\n        barChart.getData().clear();\n\n        final List<ReportPeriodUtils.Descriptor> descriptors = ReportPeriodUtils.getDescriptors(\n                periodComboBox.getValue(), startDatePicker.getValue(), endDatePicker.getValue());\n\n        // Income Series\n        final XYChart.Series<String, Number> incomeSeries = new XYChart.Series<>();\n        incomeSeries.setName(AccountType.INCOME.toString());\n        barChart.getData().add(incomeSeries);\n\n        // Expense Series\n        final XYChart.Series<String, Number> expenseSeries = new XYChart.Series<>();\n        expenseSeries.setName(AccountType.EXPENSE.toString());\n        barChart.getData().add(expenseSeries);\n\n        // Profit Series\n        final XYChart.Series<String, Number> profitSeries = new XYChart.Series<>();\n        profitSeries.setName(resources.getString(\"Word.NetIncome\"));\n        barChart.getData().add(profitSeries);\n\n        for (final ReportPeriodUtils.Descriptor descriptor : descriptors) {\n            final BigDecimal income = getSum(incomeAccounts, descriptor.getStartDate(), descriptor.getEndDate());\n            final BigDecimal expense = getSum(expenseAccounts, descriptor.getStartDate(), descriptor.getEndDate());\n\n            incomeSeries.getData().add(new XYChart.Data<>(descriptor.getLabel(), income));\n            expenseSeries.getData().add(new XYChart.Data<>(descriptor.getLabel(), expense));\n            profitSeries.getData().add(new XYChart.Data<>(descriptor.getLabel(), income.add(expense)));\n        }\n\n        int descriptorsIndex = 0;\n        for (final XYChart.Data<String, Number> data : incomeSeries.getData()) {\n            Tooltip.install(data.getNode(), new Tooltip(numberFormat.format(data.getYValue())));\n            setupPieChartLaunch(data, AccountType.INCOME,\n                    descriptors.get(descriptorsIndex).getStartDate(), descriptors.get(descriptorsIndex).getEndDate());\n\n            descriptorsIndex++;\n        }\n\n        descriptorsIndex = 0;\n        for (final XYChart.Data<String, Number> data : expenseSeries.getData()) {\n            Tooltip.install(data.getNode(), new Tooltip(numberFormat.format(data.getYValue())));\n            setupPieChartLaunch(data, AccountType.EXPENSE,\n                    descriptors.get(descriptorsIndex).getStartDate(), descriptors.get(descriptorsIndex).getEndDate());\n\n            descriptorsIndex++;\n        }\n\n        for (final XYChart.Data<String, Number> data : profitSeries.getData()) {\n            Tooltip.install(data.getNode(), new Tooltip(numberFormat.format(data.getYValue())));\n        }\n    }\n\n    private void setupPieChartLaunch(final XYChart.Data<String, Number> data, final AccountType accountType, \n            final LocalDate startDate, final LocalDate endDate){\n        //PK: Launch the PieChartNormally on click\n        data.getNode().setOnMouseClicked(event -> {\n            final FXMLUtils.Pair<IncomeExpensePieChartDialogController> pair = \n                    FXMLUtils.load(IncomeExpensePieChartDialogController.class.getResource(\"IncomeExpensePieChartDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.IncomeExpenseChart\"));\n\n            pair.getStage().show();\n\n            //PK: Now customize the data we are interested in\n            pair.getController().setParameters(accountType, startDate, endDate);\n        });\n\n        data.getNode().setOnMouseEntered(event -> data.getNode().setCursor(CustomCursor.getZoomInCursor()) );\n    }\n\n    private BigDecimal getSum(final List<Account> accounts, final LocalDate statDate, final LocalDate endDate) {\n        BigDecimal sum = BigDecimal.ZERO;\n\n        for (final Account account : accounts) {\n            sum = sum.add(account.getBalance(statDate, endDate, defaultCurrency));\n        }\n\n        return sum.negate();\n    }\n\n    @FXML\n    private void handleSaveAction() {\n        ChartUtilities.saveChart(chartPane);\n    }\n\n    @FXML\n    private void handleCopyToClipboard() {\n        ChartUtilities.copyToClipboard(chartPane);\n    }\n\n    @FXML\n    private void handlePrintAction() {\n        ChartUtilities.printChart(chartPane);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/IncomeExpensePayeePieChartDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.UUID;\nimport java.util.function.Predicate;\nimport java.util.prefs.Preferences;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.geometry.Side;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.PieChart;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.TextInputControl;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DoughnutChart;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.CollectionUtils;\nimport jgnash.util.EncodeDecode;\nimport jgnash.util.NotNull;\nimport jgnash.util.function.PayeePredicate;\n\n/**\n * Income and Expense Payee Pie Chart.\n *\n * @author Craig Cavanaugh\n * @author Pranay Kumar\n */\npublic class IncomeExpensePayeePieChartDialogController {\n\n    private static final String LAST_ACCOUNT = \"lastAccount\";\n\n    private static final String FILTERS = \"filters\";\n\n    private static final char DELIMITER = 0x25FC;\n\n    private static final int CREDIT = 0;\n\n    private static final int DEBIT = 1;\n\n    private static final int MAX_NAME_LENGTH = 12;\n\n    private static final String ELLIPSIS = \"…\";\n\n    private final Preferences preferences\n            = Preferences.userNodeForPackage(IncomeExpensePayeePieChartDialogController.class)\n            .node(\"IncomeExpensePayeePieChart\");\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private  TitledPane titledPane;\n\n    @FXML\n    private VBox filtersPane;\n\n    @FXML\n    private GridPane chartPane;\n\n    @FXML\n    private DoughnutChart debitPieChart;\n\n    @FXML\n    private DoughnutChart creditPieChart;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final ChangeListener<String> payeeChangeListener = (observable, oldValue, newValue) -> {\n        if (newValue != null) {\n            if (newValue.isEmpty()) {\n                JavaFXUtils.runLater(this::trimAuxPayeeTextFields);\n            } else {\n                if (!isEmptyPayeeFieldAvailable()) {\n                    JavaFXUtils.runLater(this::insertAuxPayeeTextField);\n                }\n            }\n        }\n        JavaFXUtils.runLater(this::updateCharts);\n\n        final List<String> filters = getPayeeTextFields().stream()\n                .filter(textField -> !textField.getText().isEmpty())\n                .map(TextInputControl::getText).collect(Collectors.toList());\n\n        if (filters.isEmpty()) {\n            preferences.remove(FILTERS);\n        } else {\n            preferences.put(FILTERS, EncodeDecode.encodeStringCollection(filters, DELIMITER));\n        }\n    };\n\n    @FXML\n    public void initialize() {\n\n        // Respect animation preference\n        debitPieChart.animatedProperty().set(Options.animationsEnabledProperty().get());\n        creditPieChart.animatedProperty().set(Options.animationsEnabledProperty().get());\n\n        creditPieChart.centerTitleProperty().set(resources.getString(\"Column.Credit\"));\n        debitPieChart.centerTitleProperty().set(resources.getString(\"Column.Debit\"));\n\n        accountComboBox.setPredicate(AccountComboBox.getShowAllPredicate());\n\n        if (preferences.get(LAST_ACCOUNT, null) != null) {\n            final String uuid = preferences.get(LAST_ACCOUNT, \"\");\n\n            if (!uuid.isEmpty()) {\n\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                final Account account = engine.getAccountByUuid(UUID.fromString(uuid));\n\n                if (account != null) {\n                    accountComboBox.setValue(account);\n                }\n            }\n        }\n\n        startDatePicker.setValue(endDatePicker.getValue().minusYears(1));\n\n        final ChangeListener<Object> listener = (observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                updateCharts();\n                preferences.put(LAST_ACCOUNT, accountComboBox.getValue().getUuid().toString());\n            }\n        };\n\n        accountComboBox.valueProperty().addListener(listener);\n        startDatePicker.valueProperty().addListener(listener);\n        endDatePicker.valueProperty().addListener(listener);\n\n        creditPieChart.setLegendSide(Side.BOTTOM);\n        debitPieChart.setLegendSide(Side.BOTTOM);\n\n        // Load in the first aux payee text field\n        insertAuxPayeeTextField();\n\n        restoreFilters();\n\n        // Expand the titled pane if filters are being used\n        JavaFXUtils.runLater(() -> {\n            if (getPayeeTextFields().size() > 1) {\n               titledPane.setExpanded(true);\n            }\n        });\n\n        // Push the initial load to the end of the platform thread for better startup and nicer visual effect\n        JavaFXUtils.runLater(this::updateCharts);\n    }\n\n    private void restoreFilters() {\n        final List<String> filters\n                = new ArrayList<>(EncodeDecode.decodeStringCollection(preferences.get(FILTERS, \"\"), DELIMITER));\n\n        // Need to reverse the order since we are inserting at the top\n        Collections.reverse(filters);\n\n        filters.forEach(this::insertAuxPayeeTextField);\n    }\n\n    private void insertAuxPayeeTextField() {\n        final TextField payeeField = new TextField();\n        payeeField.textProperty().addListener(payeeChangeListener);\n        filtersPane.getChildren().add(payeeField);\n    }\n\n    private void insertAuxPayeeTextField(final String filter) {\n        final TextField payeeField = new TextField(filter);\n        payeeField.textProperty().addListener(payeeChangeListener);\n        filtersPane.getChildren().add(0, payeeField);\n    }\n\n    private void trimAuxPayeeTextFields() {\n\n        final List<TextField> empty = filtersPane.getChildren().stream().filter(TextField.class::isInstance)\n                                                 .filter(node -> ((TextField) node).getText().isEmpty())\n                                                 .map(TextField.class::cast).collect(Collectors.toList());\n\n        // Reverse order so we leave the last empty at the bottom\n        Collections.reverse(empty);\n\n        for (int i = empty.size() - 1; i > 0; i--) {\n            final TextField textField = empty.get(i);\n\n            textField.textProperty().removeListener(payeeChangeListener);\n            filtersPane.getChildren().remove(textField);\n        }\n    }\n\n    private boolean isEmptyPayeeFieldAvailable() {\n        boolean result = false;\n\n        for (TextField textField : getPayeeTextFields()) {\n            if (textField.getText().isEmpty()) {\n                result = true;\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    private List<TextField> getPayeeTextFields() {\n        return filtersPane.getChildren().stream()\n                          .filter(TextField.class::isInstance).map(TextField.class::cast)\n                          .collect(Collectors.toList());\n    }\n\n    private void updateCharts() {\n        final Account account = accountComboBox.getValue();\n\n        if (account != null) {\n            final ObservableList<PieChart.Data>[] chartData = createPieDataSet(account);\n\n            creditPieChart.setData(chartData[CREDIT]);\n            debitPieChart.setData(chartData[DEBIT]);\n\n            final NumberFormat numberFormat = NumericFormats.getFullCommodityFormat(account.getCurrencyNode());\n\n            // Calculate the totals for percentage value\n            final double creditTotal = chartData[CREDIT].parallelStream().mapToDouble(PieChart.Data::getPieValue).sum();\n            final double debitTotal = chartData[DEBIT].parallelStream().mapToDouble(PieChart.Data::getPieValue).sum();\n\n            final NumberFormat percentFormat = NumberFormat.getPercentInstance();\n            percentFormat.setMaximumFractionDigits(1);\n            percentFormat.setMinimumFractionDigits(1);\n\n            // Install tooltips on the data after it has been added to the chart\n            creditPieChart.getData().forEach(data ->\n                    Tooltip.install(data.getNode(), new Tooltip((data.getNode().getUserData()\n                            + \"\\n\" + numberFormat.format(data.getPieValue()) + \"(\" +\n                            percentFormat.format(data.getPieValue() / creditTotal)) + \")\")));\n\n            // Install tooltips on the data after it has been added to the chart\n            debitPieChart.getData().forEach(data ->\n                    Tooltip.install(data.getNode(), new Tooltip(((data.getNode().getUserData())\n                            + \"\\n\" + numberFormat.format(data.getPieValue()) + \"(\" +\n                            percentFormat.format(data.getPieValue() / debitTotal)) + \")\")));\n\n            creditPieChart.centerSubTitleProperty().set(numberFormat.format(creditTotal));\n            debitPieChart.centerSubTitleProperty().set(numberFormat.format(debitTotal));\n        } else {\n            creditPieChart.setData(FXCollections.emptyObservableList());\n            creditPieChart.setTitle(\"No Data\");\n\n            debitPieChart.setData(FXCollections.emptyObservableList());\n            debitPieChart.setTitle(\"No Data\");\n        }\n    }\n\n\n    private ObservableList<PieChart.Data>[] createPieDataSet(@NotNull final Account account) {\n\n        @SuppressWarnings(\"unchecked\")\n        final ObservableList<PieChart.Data>[] chartData = new ObservableList[2];\n\n        chartData[CREDIT] = FXCollections.observableArrayList();\n        chartData[DEBIT] = FXCollections.observableArrayList();\n\n        final Map<String, BigDecimal> names = new HashMap<>();\n\n        final List<TranTuple> list = getTransactions(account, new ArrayList<>(), startDatePicker.getValue(),\n                endDatePicker.getValue());\n\n        final CurrencyNode currency = account.getCurrencyNode();\n\n        // Create a list of predicates\n        final List<Predicate<Transaction>> predicates = getPayeeTextFields().stream()\n                .filter(textField -> !textField.getText().isEmpty())\n                .map(textField -> new PayeePredicate(textField.getText(), Options.regexForFiltersProperty().get()))\n                .collect(Collectors.toList());\n\n        // Iterate through the list and add up filtered payees\n        for (final TranTuple tranTuple : list) {\n            final String payee = tranTuple.transaction.getPayee();\n            BigDecimal sum = tranTuple.transaction.getAmount(tranTuple.account);\n\n            sum = sum.multiply(tranTuple.account.getCurrencyNode().getExchangeRate(currency));\n\n            boolean keep = false;\n\n            if (predicates.isEmpty()) {\n                keep = true;\n            } else {\n                for (final Predicate<Transaction> predicate : predicates) {\n                    if (predicate.test(tranTuple.transaction)) {\n                        keep = true;\n                        break;\n                    }\n                }\n            }\n\n            if (keep) {\n                if (names.containsKey(payee)) {\n                    sum = sum.add(names.get(payee));\n                }\n\n                names.put(payee, sum);\n            }\n        }\n\n        final Map<String, BigDecimal> sortedNames = CollectionUtils.sortMapByValue(names);\n\n        for (final Map.Entry<String, BigDecimal> entry : sortedNames.entrySet()) {\n            final PieChart.Data data = new PieChart.Data(truncateString(entry.getKey()),\n                    entry.getValue().abs().doubleValue());\n\n            // nodes are created lazily.  Set the user data (Account) after the node is created\n            data.nodeProperty().addListener((observable, oldValue, newValue) -> newValue.setUserData(entry.getKey()));\n\n            if (entry.getValue().signum() == -1) {\n                chartData[DEBIT].add(data);\n            } else {\n                chartData[CREDIT].add(data);\n            }\n        }\n\n        return chartData;\n    }\n\n    private static String truncateString(final String string) {\n        if (string.length() <= MAX_NAME_LENGTH) {\n            return string;\n        }\n        \n\t\treturn string.substring(0, MAX_NAME_LENGTH - 1) + ELLIPSIS;\n    }\n\n    private static class TranTuple {\n\n        final Account account;\n\n        final Transaction transaction;\n\n        TranTuple(Account account, Transaction transaction) {\n            this.account = account;\n            this.transaction = transaction;\n        }\n    }\n\n    private List<TranTuple> getTransactions(final Account account, final List<TranTuple> transactions,\n                                            final LocalDate startDate, final LocalDate endDate) {\n\n        for (final Transaction transaction : account.getTransactions(startDate, endDate)) {\n            TranTuple tuple = new TranTuple(account, transaction);\n            transactions.add(tuple);\n        }\n\n        for (final Account child : account.getChildren(Comparators.getAccountByCode())) {\n            getTransactions(child, transactions, startDate, endDate);\n        }\n\n        return transactions;\n    }\n\n\n    @FXML\n    private void handleSaveAction() {\n        ChartUtilities.saveChart(chartPane);\n    }\n\n    @FXML\n    private void handleCopyToClipboard() {\n        ChartUtilities.copyToClipboard(chartPane);\n    }\n\n    @FXML\n    private void handlePrintAction() {\n        ChartUtilities.printChart(chartPane);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/IncomeExpensePieChartDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.UUID;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.geometry.Side;\nimport javafx.scene.Cursor;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.PieChart;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DoughnutChart;\nimport jgnash.uifx.resource.cursor.CustomCursor;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.function.ParentAccountPredicate;\n\n/**\n * Income and Expense Pie Chart.\n *\n * @author Craig Cavanaugh\n */\npublic class IncomeExpensePieChartDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private StackPane chartPane;\n\n    @FXML\n    private DoughnutChart pieChart;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private boolean nodeFocused = false;\n\n    private static final String LAST_ACCOUNT = \"lastAccount\";\n\n    @FXML\n    public void initialize() {\n\n        // Respect animation preference\n        pieChart.animatedProperty().set(Options.animationsEnabledProperty().get());\n\n        accountComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null && newValue.getParent().getAccountType() != AccountType.ROOT) {\n                pieChart.setCursor(CustomCursor.getZoomOutCursor());\n            } else {\n                pieChart.setCursor(Cursor.DEFAULT);\n            }\n        });\n\n        final Preferences preferences = Preferences.userNodeForPackage(IncomeExpensePieChartDialogController.class)\n                .node(\"IncomeExpensePieChart\");\n\n        accountComboBox.setPredicate(new ParentAccountPredicate());\n\n        if (preferences.get(LAST_ACCOUNT, null) != null) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            final String uuid = preferences.get(LAST_ACCOUNT, \"\");\n\n            if (!uuid.isEmpty()) {\n                final Account account = engine.getAccountByUuid(UUID.fromString(uuid));\n\n                if (account != null) {\n                    accountComboBox.setValue(account);\n                }\n            }\n        }\n\n        startDatePicker.setValue(endDatePicker.getValue().minusYears(1));\n\n        final ChangeListener<Object> listener = (observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                updateChart();\n                preferences.put(LAST_ACCOUNT, accountComboBox.getValue().getUuid().toString());\n            }\n        };\n\n        accountComboBox.valueProperty().addListener(listener);\n        startDatePicker.valueProperty().addListener(listener);\n        endDatePicker.valueProperty().addListener(listener);\n\n        pieChart.setLegendSide(Side.BOTTOM);\n\n        // zoom out\n        pieChart.setOnMouseClicked(event -> {\n            if (!nodeFocused && accountComboBox.getValue().getParent().getAccountType() != AccountType.ROOT) {\n                accountComboBox.setValue(accountComboBox.getValue().getParent());\n            }\n        });\n\n        // Push the initial load to the end of the platform thread for better startup and nicer visual effect\n        JavaFXUtils.runLater(this::updateChart);\n    }\n\n    private void updateChart() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Account a = accountComboBox.getValue();\n\n        if (a != null) {\n            final CurrencyNode defaultCurrency = a.getCurrencyNode();\n\n            final NumberFormat numberFormat = NumericFormats.getFullCommodityFormat(defaultCurrency);\n\n            final ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();\n\n            double total = a.getTreeBalance(startDatePicker.getValue(), endDatePicker.getValue(),\n                    defaultCurrency).doubleValue();\n\n            for (final Account child : a.getChildren()) {\n                double balance = child.getTreeBalance(startDatePicker.getValue(), endDatePicker.getValue(),\n                        defaultCurrency).doubleValue();\n\n                if (balance > 0 || balance < 0) {\n                    final String label = child.getName() + \" - \" + numberFormat.format(balance);\n                    final PieChart.Data data = new PieChart.Data(label, balance / total * 100);\n\n                    // nodes are created lazily.  Set the user data (Account) after the node is created\n                    data.nodeProperty().addListener((observable, oldValue, newValue) -> newValue.setUserData(child));\n\n                    pieChartData.add(data);\n                }\n            }\n\n            pieChart.setData(pieChartData);\n\n            final NumberFormat percentFormat = NumberFormat.getPercentInstance();\n            percentFormat.setMaximumFractionDigits(1);\n            percentFormat.setMinimumFractionDigits(1);\n\n            // Install tooltips on the data after it has been added to the chart\n            pieChart.getData().forEach(data ->\n                    Tooltip.install(data.getNode(), new Tooltip((((Account) data.getNode().getUserData()).getName()\n                            + \" - \" + percentFormat.format(data.getPieValue() / 100d)))));\n\n            // Indicate the node can be clicked on to zoom into the next account level\n            for (final PieChart.Data data : pieChart.getData()) {\n                data.getNode().setOnMouseEntered(event -> {\n                    final Account account = (Account) data.getNode().getUserData();\n                    if (account.isParent()) {\n                        data.getNode().setCursor(CustomCursor.getZoomInCursor());\n                    } else {\n                        data.getNode().setCursor(Cursor.DEFAULT);\n                    }\n\n                    nodeFocused = true;\n                });\n\n                data.getNode().setOnMouseExited(event -> nodeFocused = false);\n\n                // zoom in on click if this is a parent account\n                data.getNode().setOnMouseClicked(event -> {\n                    if (data.getNode().getUserData() != null) {\n                        if (((Account) data.getNode().getUserData()).isParent()) {\n                            accountComboBox.setValue((Account) data.getNode().getUserData());\n                        }\n                    }\n                });\n            }\n\n            final String title;\n\n            // pick an appropriate title\n            switch (a.getAccountType()) {\n                case EXPENSE:\n                    title = resources.getString(\"Title.PercentExpense\");\n                    break;\n                case INCOME:\n                    title = resources.getString(\"Title.PercentIncome\");\n                    break;\n                default:\n                    title = resources.getString(\"Title.PercentDist\");\n                    break;\n            }\n\n            pieChart.setTitle(title);\n\n            pieChart.centerTitleProperty().set(accountComboBox.getValue().getName());\n            pieChart.centerSubTitleProperty().set(numberFormat.format(total));\n\n            // abs() on all values won't work if children aren't of uniform sign,\n            // then again, this chart is not right to display those trees\n            //boolean negate = total != null && total.signum() < 0;\n        } else {\n            pieChart.setData(FXCollections.emptyObservableList());\n            pieChart.setTitle(\"No Data\");\n        }\n    }\n\n    void setParameters(final AccountType accountType, final LocalDate startDate, final LocalDate endDate) {\n        //PK: dates can be directly set\n        startDatePicker.setValue(startDate);\n        endDatePicker.setValue(endDate);\n\n        //PK: We assume that the root of all income or expense accounts is one account under the root. Select that\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for ( final Account comboAccount : accountComboBox.getItems() ) {\n            if ( (accountType == comboAccount.getAccountType()) && (comboAccount.getParent() == engine.getRootAccount()) ) {\n                accountComboBox.setValue(comboAccount);\n                break;\n            }\n        }\n    }\n\n    @FXML\n    private void handleSaveAction() {\n        ChartUtilities.saveChart(chartPane);\n    }\n\n    @FXML\n    private void handleCopyToClipboard() {\n        ChartUtilities.copyToClipboard(chartPane);\n    }\n\n    @FXML\n    private void handlePrintAction() {\n        ChartUtilities.printChart(chartPane);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/ListOfAccountsReport.java",
    "content": "package jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.text.MessageFormat;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Account List/Tree Report Model\n *\n * @author Craig Cavanaugh\n */\npublic class ListOfAccountsReport extends Report {\n\n    private static final String SPACE = \" \";\n\n    private static final int INDENT = 2;\n\n    public static class AccountListModel extends AbstractReportTableModel {\n\n        final CurrencyNode currencyNode;\n\n        final List<Account> accountList;\n\n        final String[] columnNames = new String[] {ResourceUtils.getString(\"Column.Account\"), ResourceUtils.getString(\"Column.Code\"),\n                ResourceUtils.getString(\"Column.Entries\"), ResourceUtils.getString(\"Column.Balance\"),\n                ResourceUtils.getString(\"Column.ReconciledBalance\"), ResourceUtils.getString(\"Column.Currency\"),\n                ResourceUtils.getString(\"Column.Type\")};\n\n        private final ColumnStyle[] columnStyles = new ColumnStyle[]{ColumnStyle.STRING, ColumnStyle.STRING,\n                ColumnStyle.STRING, ColumnStyle.BALANCE, ColumnStyle.BALANCE, ColumnStyle.STRING, ColumnStyle.STRING};\n\n        public AccountListModel(@NotNull final List<Account> accountList, @NotNull final CurrencyNode currencyNode) {\n            this.accountList = new ArrayList<>(accountList);\n            this.currencyNode = currencyNode;\n\n            this.accountList.sort(Comparators.getAccountByTreePosition(Comparators.getAccountByCode()));\n        }\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return currencyNode;\n        }\n\n        /**\n         * Returns the formatting style for the values in the column.\n         *\n         * @param columnIndex the index of the column\n         * @return the common {@code ColumnStyle} of the object values for the column\n         */\n        @Override\n        public ColumnStyle getColumnStyle(int columnIndex) {\n            return columnStyles[columnIndex];\n        }\n\n        /**\n         * Returns the column class for the values in the column.\n         *\n         * @param columnIndex the index of the column\n         * @return the common  class of the object values in the model.\n         */\n        @Override\n        public Class<?> getColumnClass(int columnIndex) {\n            if (columnIndex == 3 || columnIndex == 4) { // group column\n                return BigDecimal.class;\n            }\n\n            return String.class;\n        }\n\n        /**\n         * Returns the column count in the model.\n         *\n         * @return the number of columns in the model\n         * @see #getRowCount\n         */\n        @Override\n        public int getColumnCount() {\n            return columnNames.length;\n        }\n\n        /**\n         * Returns column name at {@code columnIndex}.\n         *\n         * @param columnIndex the index of the column\n         * @return the name of the column\n         */\n        @Override\n        public String getColumnName(int columnIndex) {\n            return columnNames[columnIndex];\n        }\n\n        /**\n         * Returns the row count in the model.\n         *\n         * @return the number of rows in the model\n         * @see #getColumnCount\n         */\n        @Override\n        public int getRowCount() {\n            return accountList.size();\n        }\n\n        /**\n         * Returns the value at {@code columnIndex} and {@code rowIndex}.\n         *\n         * @param rowIndex    the row whose value is to be queried\n         * @param columnIndex the column whose value is to be queried\n         * @return the value Object at the specified cell\n         */\n        @Override\n        public Object getValueAt(int rowIndex, int columnIndex) {\n\n            final Account account= accountList.get(rowIndex);\n\n            switch (columnIndex) {\n                case 0:\n                    return SPACE.repeat((account.getDepth() - 1) * INDENT) + account.getName();\n                case 1:\n                    return String.valueOf(account.getAccountCode());\n                case 2:\n                    return String.valueOf(account.getTransactionCount());\n                case 3:\n                    return account.getTreeBalance(LocalDate.now(), currencyNode);\n                case 4:\n                    return account.getReconciledTreeBalance();\n                case 5:\n                    return account.getCurrencyNode().getSymbol();\n                case 6:\n                    return account.getAccountType().toString();\n                default:\n                    return \"\";\n            }\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            return columnIndex != 0;\n        }\n\n        /**\n         * Returns the title for the Report\n         *\n         * @return the report title\n         */\n        @Override\n        public String getTitle() {\n            return ResourceUtils.getString(\"Title.ListOfAccounts\");\n        }\n\n        /**\n         * Returns the subtitle for the Report\n         *\n         * @return the report title\n         */\n        @Override\n        public String getSubTitle() {\n            final MessageFormat format = new MessageFormat(rb.getString(\"Pattern.Date\"));\n            return format.format(new Object[]{DateUtils.asDate(LocalDate.now())});\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/ListOfAccountsReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.IOException;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.uifx.report.pdf.ReportController;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Account Register Report Controller\n *\n * @author Craig Cavanaugh\n */\npublic class ListOfAccountsReportController implements ReportController {\n\n    private Runnable refreshRunnable = null;\n\n    private final Report report = new ListOfAccountsReport();\n\n    public ListOfAccountsReportController() {\n        super();\n    }\n\n    @FXML\n    private void initialize() {\n        // boot the report generation\n        JavaFXUtils.runLater(this::refreshReport);\n    }\n\n    @Override\n    public void setRefreshRunnable(final Runnable runnable) {\n        refreshRunnable = runnable;\n    }\n\n    @Override\n    public void getReport(final Consumer<Report> reportConsumer) {\n        reportConsumer.accept(report);\n    }\n\n    /**\n     * Forces a refresh/rebuild of the report\n     */\n    @Override\n    public void refreshReport() {\n        // send notification the report has been updated\n\n        report.clearReport();\n        try {\n            report.addTable(createReportModel());\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        if (refreshRunnable != null) {\n            refreshRunnable.run();\n        }\n    }\n\n    @Override\n    public AbstractReportTableModel createReportModel() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        return new ListOfAccountsReport.AccountListModel(engine.getAccountList(), engine.getDefaultCurrency());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/NetWorthReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport jgnash.engine.AccountGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Net Worth Report\n *\n * @author Craig Cavanaugh\n */\npublic class NetWorthReport extends AbstractSumByTypeReport {\n\n    NetWorthReport() {\n        super();\n\n        setRunningTotal(true);\n        setAddCrossTabColumn(false);\n        setForceGroupPagination(false);\n    }\n\n    @Override\n    protected List<AccountGroup> getAccountGroups() {\n        List<AccountGroup> groups = new ArrayList<>();\n\n        groups.add(AccountGroup.ASSET);\n        groups.add(AccountGroup.INVEST);\n        groups.add(AccountGroup.SIMPLEINVEST);\n        groups.add(AccountGroup.LIABILITY);\n\n        return groups;\n    }\n\n    @Override\n    public String getGrandTotalLegend() {\n        return rb.getString(\"Word.NetWorth\");\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/NetWorthReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.time.Period;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.report.pdf.ReportController;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Net Worth Report Controller\n *\n * @author Craig Cavanaugh\n */\npublic class NetWorthReportController implements ReportController {\n\n    @FXML\n    private ComboBox<Period> resolutionComboBox;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private CheckBox hideZeroBalanceAccounts;\n\n    private static final String HIDE_ZERO_BALANCE = \"hideZeroBalance\";\n\n    private static final String PERIOD = \"period\";\n\n    private static final String MONTHS = \"months\";\n\n    private final NetWorthReport report = new NetWorthReport();\n\n    private Runnable refreshRunnable = null;\n\n    private final Preferences preferences = getPreferences();\n\n    public NetWorthReportController() {\n        super();\n    }\n\n    @FXML\n    private void initialize() {\n        final Preferences preferences = getPreferences();\n\n        hideZeroBalanceAccounts.setSelected(preferences.getBoolean(HIDE_ZERO_BALANCE, true));\n\n        resolutionComboBox.getItems().setAll(Period.MONTHLY, Period.QUARTERLY, Period.YEARLY);\n        resolutionComboBox.setValue(Period.values()[preferences.getInt(PERIOD, Period.MONTHLY.ordinal())]);\n\n        resetDates();\n\n        startDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        startDatePicker.preferencesProperty().setValue(preferences);\n        startDatePicker.preferenceKeyProperty().setValue(START_DATE_KEY);\n\n        endDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        endDatePicker.preferencesProperty().setValue(preferences);\n        endDatePicker.preferenceKeyProperty().setValue(END_DATE_KEY);\n\n        startDatePicker.valueProperty().addListener((observable, oldValue, newValue) -> handleReportRefresh());\n        endDatePicker.valueProperty().addListener((observable, oldValue, newValue) -> handleReportRefresh());\n        hideZeroBalanceAccounts.onActionProperty().setValue(event -> handleReportRefresh());\n        resolutionComboBox.valueProperty().addListener((observable, oldValue, newValue) -> handleReportRefresh());\n\n        // boot the report generation\n        JavaFXUtils.runLater(this::refreshReport);\n    }\n\n    @Override\n    public void setRefreshRunnable(final Runnable runnable) {\n        refreshRunnable = runnable;\n    }\n\n    @Override\n    public void getReport(final Consumer<Report> reportConsumer) {\n        reportConsumer.accept(report);\n    }\n\n    @Override\n    public void refreshReport() {\n        handleReportRefresh();\n    }\n\n    private void handleReportRefresh() {\n\n        preferences.putBoolean(HIDE_ZERO_BALANCE, hideZeroBalanceAccounts.isSelected());\n        preferences.putInt(MONTHS, DateUtils.getLastDayOfTheMonths(startDatePicker.getValue(),\n                endDatePicker.getValue()).size());\n        preferences.putInt(PERIOD, resolutionComboBox.getValue().ordinal());\n\n        addTable();\n\n        // send notification the report has been updated\n        if (refreshRunnable != null) {\n            refreshRunnable.run();\n        }\n    }\n\n    private void resetDates() {\n\n        // calculate number of months for 4-5 columns\n        final int months = (int)(resolutionComboBox.getValue().getMonths() * 4);\n\n        endDatePicker.setValue(LocalDate.now());\n        startDatePicker.setValue(DateUtils.getFirstDayOfTheMonth(LocalDate.now().minusMonths(months - 1)));\n    }\n\n    private void addTable() {\n        final AbstractReportTableModel model = createReportModel();\n\n        report.clearReport();\n        report.setTitle(ResourceUtils.getString(\"Word.NetWorth\"));\n\n        try {\n            report.addTable(model);\n            report.addFooter();\n        } catch (final IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public AbstractReportTableModel createReportModel() {\n        report.setReportPeriod(resolutionComboBox.getValue());\n\n        return report.createReportModel(startDatePicker.getValue(), endDatePicker.getValue(),\n                hideZeroBalanceAccounts.isSelected());\n    }\n\n    @FXML\n    private void handleResetAll() {\n        resetDates();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/PortfolioReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.text.MessageFormat;\nimport java.time.LocalDate;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentPerformanceSummary;\nimport jgnash.engine.SecurityNode;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Portfolio Report\n *\n * @author Craig Cavanaugh\n */\npublic class PortfolioReport extends Report {\n\n    private static final int COLUMN_COUNT = 12;\n\n    PortfolioReport() {\n        super();\n\n        setForceGroupPagination(false);\n    }\n\n    static int getColumnCount() {\n        return COLUMN_COUNT;\n    }\n\n    static String getColumnName(final int columnIndex) {\n        switch (columnIndex) {\n            case 0:\n                return rb.getString(\"Column.Security\");\n            case 1:\n                return rb.getString(\"Column.Short.Quantity\");\n            case 2:\n                return rb.getString(\"Column.CostBasis\");\n            case 3:\n                return rb.getString(\"Column.TotalCostBasis\");\n            case 4:\n                return rb.getString(\"Column.Price\");\n            case 5:\n                return rb.getString(\"Column.MktValue\");\n            case 6:\n                return rb.getString(\"Column.Short.UnrealizedGain\");\n            case 7:\n                return rb.getString(\"Column.Short.RealizedGain\");\n            case 8:\n                return rb.getString(\"Column.Short.TotalGain\");\n            case 9:\n                return rb.getString(\"Column.Short.TotalGainPercentage\");\n            case 10:\n                return rb.getString(\"Column.Short.InternalRateOfReturn\");\n            case 11:\n                return rb.getString(\"Column.Short.PercentagePortfolio\");\n            default:\n                return \"ERR\";\n        }\n    }\n\n    static AbstractReportTableModel createReportModel(final Account account, final LocalDate startDate,\n                                                      final LocalDate endDate, final boolean recursive,\n                                                      final boolean longNames,\n                                                      final Function<String, Boolean> columnVisibilityFunction) {\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        return new PortfolioReportTableModel(account, engine.getDefaultCurrency(), startDate, endDate, recursive,\n                longNames, columnVisibilityFunction);\n    }\n\n    private static class PortfolioReportTableModel extends AbstractReportTableModel {\n\n        private final Account account;\n\n        private final CurrencyNode baseCurrency;\n\n        private InvestmentPerformanceSummary performanceSummary;\n\n        private final boolean longNames;\n\n        private final String subTitle;\n\n        private final Function<String, Boolean> columnVisibilityFunction;\n\n        PortfolioReportTableModel(@NotNull final Account account, @NotNull final CurrencyNode baseCurrency,\n                                  final LocalDate startDate, final LocalDate endDate, final boolean recursive,\n                                  final boolean longNames, final Function<String, Boolean> columnVisibilityFunction) {\n            Objects.requireNonNull(account);\n            Objects.requireNonNull(baseCurrency);\n\n            this.account = account;\n            this.baseCurrency = baseCurrency;\n            this.longNames = longNames;\n\n            this.columnVisibilityFunction = Objects.requireNonNullElseGet(columnVisibilityFunction, () -> s -> true);\n\n            // update the subtitle\n            final MessageFormat format = new MessageFormat(rb.getString(\"Pattern.DateRange\"));\n            subTitle = format.format(new Object[]{DateUtils.asDate(startDate), DateUtils.asDate(endDate)});\n\n            try {\n                performanceSummary = new InvestmentPerformanceSummary(account, startDate, endDate, recursive);\n\n                performanceSummary.runCalculations();\n\n                Logger.getLogger(PortfolioReport.class.getName()).info(performanceSummary.toString());\n            } catch (final Exception e) {\n                Logger.getLogger(PortfolioReport.class.getName()).log(Level.SEVERE, null, e);\n            }\n        }\n\n        @Override\n        public boolean isColumnVisible(final int column) {\n\n            // super can override\n            boolean result  = super.isColumnVisible(column);\n\n            if (column != 0 && result) {    // security name is not an option\n                result = columnVisibilityFunction.apply(getColumnName(column));\n            }\n\n            return result;\n        }\n\n        @Override\n        public String getTitle() {\n            return account.getName() + \" - \" + rb.getString(\"Title.PortfolioReport\");\n        }\n\n        @Override\n        public String getSubTitle() {\n            return subTitle;\n        }\n\n        @Override\n        public int getRowCount() {\n            return performanceSummary.getSecurities().size();\n        }\n\n        @Override\n        public Object getValueAt(final int row, final int col) {\n            SecurityNode cn = performanceSummary.getSecurities().get(row);\n\n            InvestmentPerformanceSummary.SecurityPerformanceData pd = performanceSummary.getPerformanceData(cn);\n\n            switch (col) {\n                case 0:\n                    if (longNames) {\n                        return pd.getNode().getDescription();\n                    }\n                    return pd.getNode().getSymbol();\n                case 1:\n                    return pd.getSharesHeld();\n                case 2:\n                    return pd.getCostBasisPerShare();\n                case 3:\n                    return pd.getHeldCostBasis();\n                case 4:\n                    return pd.getPrice(baseCurrency);\n                case 5:\n                    return pd.getMarketValue(baseCurrency);\n                case 6:\n                    return pd.getUnrealizedGains();\n                case 7:\n                    return pd.getRealizedGains();\n                case 8:\n                    return pd.getTotalGains();\n                case 9:\n                    return pd.getTotalGainsPercentage();\n                case 10:\n                    double irr = pd.getInternalRateOfReturn();\n                    return (Double.isNaN(irr)) ? null : BigDecimal.valueOf(irr);\n                case 11:\n                    return pd.getPercentPortfolio();\n                case 12:\n                    return AbstractReportTableModel.DEFAULT_GROUP;\n                default:\n                    return \"ERR\";\n            }\n        }\n\n        @Override\n        public String getColumnName(final int columnIndex) {\n            if (columnIndex >= getColumnCount()) {\n                return \"group\";\n            }\n\n            return PortfolioReport.getColumnName(columnIndex);\n        }\n        @Override\n        public int getColumnCount() {\n            return PortfolioReport.getColumnCount() + 1;    // add group column\n        }\n\n        @Override\n        public Class<?> getColumnClass(final int columnIndex) {\n            if (columnIndex == 0 || columnIndex == 12) {\n                return String.class;\n            }\n\n            return BigDecimal.class;\n        }\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return baseCurrency;\n        }\n\n        @Override\n        public ColumnStyle getColumnStyle(final int columnIndex) {\n            switch (columnIndex) {\n                case 1:\n                    return ColumnStyle.QUANTITY;\n                case 2:\n                case 4:\n                    return ColumnStyle.BALANCE;\n                case 3:\n                case 5:\n                case 6:\n                case 7:\n                case 8:\n                    return ColumnStyle.AMOUNT_SUM;\n                case 9:\n                case 10:\n                case 11:\n                    return ColumnStyle.PERCENTAGE;\n                case 12:\n                    return ColumnStyle.GROUP_NO_HEADER;\n                case 0:\n                default:\n                    return ColumnStyle.STRING;\n            }\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            return columnIndex > 0;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/PortfolioReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\n\nimport jgnash.engine.AccountType;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentPerformanceSummary;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.CheckComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.report.pdf.ReportController;\nimport jgnash.uifx.util.JavaFXUtils;\n\nimport org.apache.commons.lang3.tuple.Pair;\n\n/**\n * Portfolio report controller.\n *\n * @author Craig Cavanaugh\n */\npublic class PortfolioReportController implements ReportController {\n\n    private static final String RECURSIVE = \"recursive\";\n\n    private static final String VERBOSE = \"verbose\";\n\n    private static final String REPORT_COLUMNS = \"reportColumns\";\n\n    @FXML\n    private CheckComboBox<String> columnComboBox;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private CheckBox subAccountCheckBox;\n\n    @FXML\n    private CheckBox longNameCheckBox;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private Runnable refreshRunnable = null;\n\n    private final Report report = new PortfolioReport();\n\n    private ChangeListener<Object> changeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Boolean> comboChangeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Object> accountChangeListener;\n\n    @FXML\n    private void initialize() {\n        final Preferences preferences = getPreferences();\n\n        initColumnCombo();\n\n        comboChangeListener = (observable, oldValue, newValue) -> {\n            final List<String> columns = columnComboBox.getCheckedItems();\n\n            if (columns.size() > 0) {\n                preferences.put(REPORT_COLUMNS, String.join(\",\", columns));\n                System.out.println(String.join(\",\", columns));\n            } else {\n                preferences.put(REPORT_COLUMNS, \"\");\n            }\n\n            handleReportRefresh();\n        };\n\n        // list to changes and update preferences and the report\n        columnComboBox.addListener(new WeakChangeListener<>(comboChangeListener));\n\n        // Only show visible investment accounts\n        accountComboBox.setPredicate(account -> account.instanceOf(AccountType.INVEST) && account.isVisible());\n\n        subAccountCheckBox.setSelected(preferences.getBoolean(RECURSIVE, true));\n        longNameCheckBox.setSelected(preferences.getBoolean(VERBOSE, false));\n\n        // set date range\n        updateDateRanges();\n\n        startDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        startDatePicker.preferencesProperty().setValue(preferences);\n        startDatePicker.preferenceKeyProperty().setValue(START_DATE_KEY);\n\n        endDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        endDatePicker.preferencesProperty().setValue(preferences);\n        endDatePicker.preferenceKeyProperty().setValue(END_DATE_KEY);\n\n        changeListener = (observable, oldValue, newValue) -> {\n            if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {   // could be null if GC is slow\n                handleReportRefresh();\n            }\n        };\n\n        // update data ranges prior to refresh\n        accountChangeListener = (observable, oldValue, newValue) -> {\n            updateDateRanges();\n            changeListener.changed(observable, oldValue, newValue);\n        };\n\n        subAccountCheckBox.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n        longNameCheckBox.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n        accountComboBox.valueProperty().addListener(new WeakChangeListener<>(accountChangeListener));\n        startDatePicker.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n        endDatePicker.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n\n        // boot the report generation\n        JavaFXUtils.runLater(this::refreshReport);\n    }\n\n    private void initColumnCombo() {\n        final List<String> columnNames = getAvailColumns();\n\n        for (final String columnName : columnNames) {\n            columnComboBox.add(columnName, false);\n        }\n\n        final String availColumns = getPreferences().get(REPORT_COLUMNS, String.join(\",\", columnNames));\n\n        if (availColumns != null && !availColumns.isBlank()) {\n            String[] columns = availColumns.split(\",\");\n\n            for (String column : columns) {\n                columnComboBox.setChecked(column, true);\n            }\n        }\n    }\n\n    @FXML\n    private void handleResetAll() {\n        columnComboBox.setAllChecked();\n\n        JavaFXUtils.runLater(this::updateDateRanges);\n    }\n\n    private void updateDateRanges() {\n        if (accountComboBox.getValue() != null) {\n\n            final Pair<LocalDate, LocalDate> dateRange\n                    = InvestmentPerformanceSummary.getTransactionDateRange(accountComboBox.getValue(),\n                    subAccountCheckBox.isSelected());\n\n            startDatePicker.setValue(dateRange.getLeft());\n            endDatePicker.setValue(dateRange.getRight());\n        }\n    }\n\n    @Override\n    public void setRefreshRunnable(final Runnable runnable) {\n        refreshRunnable = runnable;\n    }\n\n\n    @Override\n    public void getReport(Consumer<Report> reportConsumer) {\n        reportConsumer.accept(report);\n    }\n\n    @Override\n    public void refreshReport() {\n        handleReportRefresh();\n    }\n\n    private void handleReportRefresh() {\n\n        final Preferences preferences = getPreferences();\n\n        preferences.putBoolean(RECURSIVE, subAccountCheckBox.isSelected());\n        preferences.putBoolean(VERBOSE, longNameCheckBox.isSelected());\n\n        // make sure an account is available and the value is not null due to slow GC\n        if (!accountComboBox.isDisabled() && accountComboBox.getValue() != null) {\n            addTable();\n\n            // send notification the report has been updated\n            if (refreshRunnable != null) {\n                refreshRunnable.run();\n            }\n        }\n    }\n\n    private void addTable() {\n        final AbstractReportTableModel model = createReportModel();\n\n        report.clearReport();\n\n        try {\n            report.addTable(model);\n            report.addFooter();\n        } catch (final IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public AbstractReportTableModel createReportModel() {\n        final List<String> availableColumns = columnComboBox.getCheckedItems();\n\n        return PortfolioReport.createReportModel(accountComboBox.getValue(), startDatePicker.getValue(),\n                endDatePicker.getValue(), subAccountCheckBox.isSelected(), longNameCheckBox.isSelected(),\n                availableColumns::contains);\n    }\n\n    private List<String> getAvailColumns() {\n        final List<String> availColumns = new ArrayList<>();\n\n        for (int i = 1; i < PortfolioReport.getColumnCount(); i++) {\n            availColumns.add(PortfolioReport.getColumnName(i));\n        }\n\n        return availColumns;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/ProfitLossReport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport jgnash.engine.AccountGroup;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Profit and Loss Report\n *\n * @author Craig Cavanaugh\n */\npublic class ProfitLossReport extends AbstractSumByTypeReport {\n\n    ProfitLossReport() {\n        super();\n\n        setRunningTotal(false);\n        setAddCrossTabColumn(true);\n        setForceGroupPagination(false);\n    }\n\n    @Override\n    protected List<AccountGroup> getAccountGroups() {\n        final List<AccountGroup> groups = new ArrayList<>();\n\n        groups.add(AccountGroup.INCOME);\n        groups.add(AccountGroup.EXPENSE);\n\n        return groups;\n    }\n\n    @Override\n    public String getGrandTotalLegend() {\n        return rb.getString(\"Word.NetIncome\");\n    }\n\n    @Override\n    public String getGroupFooterLabel() {\n        return rb.getString(\"Word.Subtotal\");\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/ProfitLossReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\n\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.SortOrder;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.time.Period;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.report.pdf.ReportController;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Profit Loss Report Controller\n *\n * @author Craig Cavanaugh\n */\npublic class ProfitLossReportController implements ReportController {\n\n    @FXML\n    private ComboBox<Period> resolutionComboBox;\n\n    @FXML\n    private ComboBox<SortOrder> sortOrderComboBox;\n\n    @FXML\n    private CheckBox showLongNamesCheckBox;\n\n    @FXML\n    private CheckBox showAccountPercentages;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private CheckBox hideZeroBalanceAccounts;\n\n    private static final String HIDE_ZERO_BALANCE = \"hideZeroBalance\";\n\n    private static final String PERIOD = \"period\";\n\n    private static final String MONTHS = \"months\";\n\n    private static final String SHOW_FULL_ACCOUNT_PATH = \"showFullAccountPath\";\n\n    private static final String SHOW_PERCENTAGES = \"showPercentages\";\n\n    private static final String SORT_ORDER = \"sortOrder\";\n\n    private final ProfitLossReport report = new ProfitLossReport();\n\n    private Runnable refreshRunnable = null;\n\n    private final Preferences preferences = getPreferences();\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Object> changeListener;  // need to hold a reference to prevent premature collection\n\n    public ProfitLossReportController() {\n        super();\n    }\n\n    @FXML\n    private void initialize() {\n        final Preferences preferences = getPreferences();\n\n        hideZeroBalanceAccounts.setSelected(preferences.getBoolean(HIDE_ZERO_BALANCE, true));\n\n        showLongNamesCheckBox.setSelected(preferences.getBoolean(SHOW_FULL_ACCOUNT_PATH, false));\n        showAccountPercentages.setSelected(preferences.getBoolean(SHOW_PERCENTAGES, false));\n\n        resolutionComboBox.getItems().setAll(Period.MONTHLY, Period.QUARTERLY, Period.YEARLY);\n        resolutionComboBox.setValue(Period.values()[preferences.getInt(PERIOD, Period.QUARTERLY.ordinal())]);\n\n        resetDates();\n\n        startDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        startDatePicker.preferencesProperty().setValue(preferences);\n        startDatePicker.preferenceKeyProperty().setValue(START_DATE_KEY);\n\n        endDatePicker.preserveDateProperty().bind(Options.restoreReportDateProperty());\n        endDatePicker.preferencesProperty().setValue(preferences);\n        endDatePicker.preferenceKeyProperty().setValue(END_DATE_KEY);\n\n        sortOrderComboBox.getItems().setAll(SortOrder.values());\n        sortOrderComboBox.setValue(SortOrder.values()[preferences.getInt(SORT_ORDER, SortOrder.BY_NAME.ordinal())]);\n\n        // change listener is assigned after controls have been set to prevent multiple report refreshes\n        changeListener = (observable, oldValue, newValue) -> handleReportRefresh();\n\n        startDatePicker.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n        endDatePicker.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n        resolutionComboBox.valueProperty().addListener(new WeakChangeListener<>(changeListener));\n        hideZeroBalanceAccounts.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n        showLongNamesCheckBox.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n        showAccountPercentages.selectedProperty().addListener(new WeakChangeListener<>(changeListener));\n\n        // boot the report generation\n        JavaFXUtils.runLater(this::refreshReport);\n    }\n\n    @Override\n    public void setRefreshRunnable(final Runnable runnable) {\n        refreshRunnable = runnable;\n    }\n\n    @Override\n    public void getReport(final Consumer<Report> reportConsumer) {\n        reportConsumer.accept(report);\n    }\n\n    @Override\n    public void refreshReport() {\n        handleReportRefresh();\n    }\n\n    private void handleReportRefresh() {\n\n        preferences.putBoolean(HIDE_ZERO_BALANCE, hideZeroBalanceAccounts.isSelected());\n        preferences.putInt(MONTHS, DateUtils.getLastDayOfTheMonths(startDatePicker.getValue(),\n                endDatePicker.getValue()).size());\n        preferences.putInt(PERIOD, resolutionComboBox.getValue().ordinal());\n        preferences.putInt(SORT_ORDER, sortOrderComboBox.getValue().ordinal());\n        preferences.putBoolean(SHOW_FULL_ACCOUNT_PATH, showLongNamesCheckBox.isSelected());\n        preferences.getBoolean(SHOW_PERCENTAGES, showAccountPercentages.isSelected());\n\n        addTable();\n\n        // send notification the report has been updated\n        if (refreshRunnable != null) {\n            refreshRunnable.run();\n        }\n    }\n\n    private void resetDates() {\n\n        // calculate number of months for 4-5 columns\n        final int months = (int)(resolutionComboBox.getValue().getMonths() * 4);\n\n        endDatePicker.setValue(LocalDate.now());\n        startDatePicker.setValue(DateUtils.getFirstDayOfTheMonth(LocalDate.now().minusMonths(months - 1)));\n    }\n\n    private void addTable() {\n        AbstractReportTableModel model = createReportModel();\n\n        report.clearReport();\n        report.setTitle(ResourceUtils.getString(\"Title.ProfitLoss\"));\n\n\n        try {\n            report.addTable(model);\n            report.addFooter();\n        } catch (final IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public AbstractReportTableModel createReportModel() {\n        report.setAddPercentileColumn(showAccountPercentages.isSelected());\n\n        report.setSortOrder(sortOrderComboBox.getValue());\n        report.setReportPeriod(resolutionComboBox.getValue());\n\n        report.setShowFullAccountPath(showLongNamesCheckBox.isSelected());\n\n        return report.createReportModel(startDatePicker.getValue(), endDatePicker.getValue(),\n                hideZeroBalanceAccounts.isSelected());\n    }\n\n    @FXML\n    public void handleRefresh() {\n        JavaFXUtils.runLater(this::refreshReport);\n    }\n\n    @FXML\n    private void handleResetAll() {\n        resetDates();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/ReportActions.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.io.File;\nimport java.time.LocalDate;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.prefs.Preferences;\n\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.report.BalanceByMonthCSVReport;\nimport jgnash.report.ProfitLossTextReport;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.control.DateRangeDialogController;\nimport jgnash.uifx.report.pdf.ReportViewerDialogController;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.Nullable;\n\n/**\n * Utility class for loading and displaying reports.\n *\n * @author Craig Cavanaugh\n */\npublic class ReportActions {\n\n    private static final String LAST_DIR = \"lastDir\";\n    private static final String FORCE_CURRENCY = \"forceCurrency\";\n\n    public static void displayAccountBalanceChart() {\n        final FXMLUtils.Pair<AccountBalanceChartController> pair =\n                FXMLUtils.load(IncomeExpenseBarChartDialogController.class.getResource(\"AccountBalanceChart.fxml\"),\n                        ResourceUtils.getString(\"Title.AccountBalance\"));\n\n        pair.getStage().show();\n    }\n\n    public static void displayListOfAccountsReport() {\n        final FXMLUtils.Pair<ReportViewerDialogController> reportPair =\n                FXMLUtils.load(ReportViewerDialogController.class.getResource(\"ReportViewerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.ListOfAccounts\"));\n\n        reportPair.getController().loadReportController(\"ListOfAccountsReport.fxml\");\n\n        // Preserve size and location\n        StageUtils.addBoundsListener(reportPair.getStage(), ListOfAccountsReportController.class, MainView.getPrimaryStage());\n\n        reportPair.getStage().show();\n    }\n\n    public static void displayAccountRegisterReport(@Nullable final Account account) {\n        final FXMLUtils.Pair<ReportViewerDialogController> reportPair =\n                FXMLUtils.load(ReportViewerDialogController.class.getResource(\"ReportViewerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.AccountRegister\"));\n\n        final AccountRegisterReportController controller\n                = reportPair.getController().loadReportController(\"AccountRegisterReport.fxml\");\n\n        if (controller != null) {\n            controller.setAccount(account);\n        }\n\n        // Preserve size and location\n        StageUtils.addBoundsListener(reportPair.getStage(), AccountRegisterReportController.class, MainView.getPrimaryStage());\n\n        reportPair.getStage().show();\n    }\n\n    public static void displayIncomeExpensePieChart() {\n        final FXMLUtils.Pair<IncomeExpensePieChartDialogController> pair =\n                FXMLUtils.load(IncomeExpensePieChartDialogController.class.getResource(\"IncomeExpensePieChartDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.IncomeExpenseChart\"));\n\n        pair.getStage().show();\n    }\n\n    public static void displayIncomeExpensePayeePieChart() {\n        final FXMLUtils.Pair<IncomeExpensePayeePieChartDialogController> pair =\n                FXMLUtils.load(IncomeExpensePayeePieChartDialogController.class.getResource(\"IncomeExpensePayeePieChartDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.IncomeExpenseChart\"));\n\n        pair.getStage().show();\n    }\n\n    public static void displayIncomeExpenseBarChart() {\n        final FXMLUtils.Pair<IncomeExpenseBarChartDialogController> pair =\n                FXMLUtils.load(IncomeExpenseBarChartDialogController.class.getResource(\"IncomeExpenseBarChartDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.IncomeExpenseBarChart\"));\n\n        pair.getStage().show();\n    }\n\n    public static void displayTransactionTagPieChart() {\n        final FXMLUtils.Pair<TransactionTagPieChartDialogController> pair =\n                FXMLUtils.load(TransactionTagPieChartDialogController.class.getResource(\"TransactionTagPieChartDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.TransactionTagPieChart\"));\n\n        pair.getStage().show();\n    }\n\n    public static void displayPortfolioReport() {\n        final FXMLUtils.Pair<ReportViewerDialogController> reportPair =\n                FXMLUtils.load(ReportViewerDialogController.class.getResource(\"ReportViewerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.PortfolioReport\"));\n\n        reportPair.getController().loadReportController(\"PortfolioReport.fxml\");\n\n        // Preserve size and location\n        StageUtils.addBoundsListener(reportPair.getStage(), PortfolioReportController.class, MainView.getPrimaryStage());\n\n        reportPair.getStage().show();\n    }\n\n    public static void displayProfitLossReport() {\n        final FXMLUtils.Pair<ReportViewerDialogController> reportPair =\n                FXMLUtils.load(ReportViewerDialogController.class.getResource(\"ReportViewerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.ProfitLoss\"));\n\n        reportPair.getController().loadReportController(\"ProfitLossReport.fxml\");\n\n        // Preserve size and location\n        StageUtils.addBoundsListener(reportPair.getStage(), ProfitLossReportController.class, MainView.getPrimaryStage());\n\n        reportPair.getStage().show();\n    }\n\n    public static void displayBalanceSheetReport() {\n        final FXMLUtils.Pair<ReportViewerDialogController> reportPair =\n                FXMLUtils.load(ReportViewerDialogController.class.getResource(\"ReportViewerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.BalanceSheet\"));\n\n        reportPair.getController().loadReportController(\"BalanceSheetReport.fxml\");\n\n        // Preserve size and location\n        StageUtils.addBoundsListener(reportPair.getStage(), BalanceSheetReportController.class, MainView.getPrimaryStage());\n\n        reportPair.getStage().show();\n    }\n\n    public static void displayNetWorthReport() {\n        final FXMLUtils.Pair<ReportViewerDialogController> reportPair =\n                FXMLUtils.load(ReportViewerDialogController.class.getResource(\"ReportViewerDialog.fxml\"),\n                        ResourceUtils.getString(\"Word.NetWorth\"));\n\n        reportPair.getController().loadReportController(\"NetWorthReport.fxml\");\n\n        // Preserve size and location\n        StageUtils.addBoundsListener(reportPair.getStage(), NetWorthReportController.class, MainView.getPrimaryStage());\n\n        reportPair.getStage().show();\n    }\n\n    public static void exportProfitLossReport() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final CurrencyNode baseCommodity = engine.getDefaultCurrency();\n\n        final FXMLUtils.Pair<DateRangeDialogController> pair\n                = FXMLUtils.load(DateRangeDialogController.class.getResource(\"DateRangeDialog.fxml\"),\n                ResourceUtils.getString(\"Title.ReportOptions\"));\n\n        pair.getStage().setResizable(false);\n        pair.getStage().showAndWait();\n\n        final Optional<LocalDate[]> optional = pair.getController().getDates();\n\n        optional.ifPresent(localDates -> {\n\n            final Preferences pref = Preferences.userNodeForPackage(ReportActions.class);\n\n            final FileChooser fileChooser = new FileChooser();\n            fileChooser.setTitle(ResourceUtils.getString(\"Title.SaveFile\"));\n\n            final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n            // Protect against an IllegalArgumentException\n            if (initialDirectory.isDirectory()) {\n                fileChooser.setInitialDirectory(initialDirectory);\n            }\n\n            fileChooser.getExtensionFilters().addAll(\n                    new FileChooser.ExtensionFilter(\"TXT\", \"*.txt\")\n            );\n\n            final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n            if (file != null) {\n                pref.put(LAST_DIR, file.getParent());\n\n                final ProfitLossTextReport report = new ProfitLossTextReport(file.getAbsolutePath(), localDates[0],\n                        localDates[1], baseCommodity, AccountBalanceDisplayManager::convertToSelectedBalanceMode);\n\n                report.run();\n            }\n        });\n    }\n\n    public static void exportBalanceByMonthCSVReport() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Preferences preferences = Preferences.userNodeForPackage(ReportActions.class);\n\n        final FXMLUtils.Pair<BalanceByMonthOptionsDialogController> pair\n                = FXMLUtils.load(BalanceByMonthOptionsDialogController.class.getResource(\"BalanceByMonthOptionsDialog.fxml\"),\n                ResourceUtils.getString(\"Title.ReportOptions\"));\n\n        pair.getController().forceDefaultCurrencyProperty().set(preferences.getBoolean(FORCE_CURRENCY, false));\n        pair.getStage().setResizable(false);\n        pair.getStage().showAndWait();\n\n        final boolean vertical = pair.getController().isVertical();\n        final boolean forceCurrency = pair.getController().forceDefaultCurrencyProperty().get();\n\n        final Optional<LocalDate[]> optional = pair.getController().getDates();\n\n        optional.ifPresent(localDates -> {\n            final String lastDir = preferences.get(LAST_DIR, null);\n            preferences.putBoolean(FORCE_CURRENCY, forceCurrency);\n\n            final FileChooser fileChooser = new FileChooser();\n            fileChooser.setTitle(ResourceUtils.getString(\"Title.SaveFile\"));\n\n            if (lastDir != null) {\n                fileChooser.setInitialDirectory(new File(lastDir));\n            }\n\n            fileChooser.getExtensionFilters().addAll(\n                    new FileChooser.ExtensionFilter(\"CSV\", \"*.csv\")\n            );\n\n            final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n            if (file != null) {\n                preferences.put(LAST_DIR, file.getParent());\n\n                final BalanceByMonthCSVReport report;\n\n                if (forceCurrency) {\n                    report = new BalanceByMonthCSVReport(file.getAbsolutePath(), localDates[0], localDates[1],\n                            engine.getDefaultCurrency(), vertical,\n                            AccountBalanceDisplayManager::convertToSelectedBalanceMode);\n\n                } else {\n                    report = new BalanceByMonthCSVReport(file.getAbsolutePath(), localDates[0],\n                            localDates[1], null, vertical, AccountBalanceDisplayManager::convertToSelectedBalanceMode);\n                }\n\n                report.run();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/TransactionTagPieChartDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.math.MathContext;\nimport java.text.NumberFormat;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.UUID;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.geometry.Side;\nimport javafx.scene.Scene;\nimport javafx.scene.chart.PieChart;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Tag;\nimport jgnash.engine.Transaction;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DoughnutChart;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.function.ParentAccountPredicate;\n\n/**\n * Transaction Tag Pie Chart.\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionTagPieChartDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private StackPane chartPane;\n\n    @FXML\n    private DoughnutChart pieChart;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private static final String LAST_ACCOUNT = \"lastAccount\";\n\n    private final BigDecimal ONE_HUNDRED = new BigDecimal(\"100\");\n\n    @FXML\n    public void initialize() {\n\n        final Preferences preferences = Preferences.userNodeForPackage(TransactionTagPieChartDialogController.class)\n                .node(\"TransactionTagPieChart\");\n\n        // Respect animation preference\n        pieChart.animatedProperty().set(Options.animationsEnabledProperty().get());\n\n        accountComboBox.setPredicate(new ParentAccountPredicate());\n\n        if (preferences.get(LAST_ACCOUNT, null) != null) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            final String uuid = preferences.get(LAST_ACCOUNT, \"\");\n\n            if (!uuid.isEmpty()) {\n                final Account account = engine.getAccountByUuid(UUID.fromString(uuid));\n\n                if (account != null) {\n                    accountComboBox.setValue(account);\n                }\n            }\n        }\n\n        startDatePicker.setValue(endDatePicker.getValue().minusYears(1));\n\n        final ChangeListener<Object> listener = (observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                updateChart();\n                preferences.put(LAST_ACCOUNT, accountComboBox.getValue().getUuid().toString());\n            }\n        };\n\n        accountComboBox.valueProperty().addListener(listener);\n        startDatePicker.valueProperty().addListener(listener);\n        endDatePicker.valueProperty().addListener(listener);\n\n        pieChart.setLegendSide(Side.BOTTOM);\n\n        // Push the initial load to the end of the platform thread for better startup and nicer visual effect\n        JavaFXUtils.runLater(this::updateChart);\n    }\n\n    private void updateChart() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Account a = accountComboBox.getValue();\n\n        if (a != null) {\n            final CurrencyNode defaultCurrency = a.getCurrencyNode();\n\n            final NumberFormat numberFormat = NumericFormats.getFullCommodityFormat(defaultCurrency);\n\n            final ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();\n\n            final Map<Tag, BigDecimal> balanceMap = new HashMap<>();\n\n            // Iterate through all the Tags in use\n            for (final Tag tag : engine.getTagsInUse()) {\n\n                BigDecimal balance = new BigDecimal(BigInteger.ZERO);\n\n                for (final Account child : a.getChildren()) {\n                    balance = balance.add(getSumForTag(tag, child));\n                }\n\n                if (balance.compareTo(BigDecimal.ZERO) != 0) {\n                    balanceMap.put(tag, balance);\n                }\n            }\n\n            // Sum of all the balances\n            final BigDecimal total = balanceMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);\n\n            // Iterate and crate each pie slice\n            for (final Map.Entry<Tag, BigDecimal> entry : balanceMap.entrySet()) {\n                final Tag tag = entry.getKey();\n                final BigDecimal balance = entry.getValue();\n\n                final String label = tag.getName() + \" - \" + numberFormat.format(balance.doubleValue());\n\n                // protect against a div by zero caused by net zero of income and expense\n                double value = total.compareTo(BigDecimal.ZERO) == 0 ? 0 :\n                        balance.divide(total, MathContext.DECIMAL64).multiply(ONE_HUNDRED).doubleValue();\n\n                final PieChart.Data data = new PieChart.Data(label, value);\n\n                // nodes are created lazily.  Set the user data (Tag) after the node is created\n                data.nodeProperty().addListener((observable, oldValue, newValue) -> newValue.setUserData(tag));\n\n                pieChartData.add(data);\n            }\n\n            pieChart.setData(pieChartData);\n\n            final NumberFormat percentFormat = NumberFormat.getPercentInstance();\n            percentFormat.setMaximumFractionDigits(1);\n            percentFormat.setMinimumFractionDigits(1);\n\n            // Install tooltips on the data after it has been added to the chart\n            pieChart.getData().forEach(data ->\n                    Tooltip.install(data.getNode(), new Tooltip((((Tag) data.getNode().getUserData()).getName()\n                            + \" - \" + percentFormat.format(data.getPieValue() / 100d)))));\n\n            pieChart.setTitle(resources.getString(\"Title.TransactionTagPieChart\"));\n\n            pieChart.centerTitleProperty().set(accountComboBox.getValue().getName());\n            pieChart.centerSubTitleProperty().set(numberFormat.format(total));\n        } else {\n            pieChart.setData(FXCollections.emptyObservableList());\n            pieChart.setTitle(\"No Data\");\n        }\n    }\n\n    private BigDecimal getSumForTag(final Tag tag, final Account account) {\n\n        BigDecimal sum = BigDecimal.ZERO;\n\n        for (final Transaction transaction : account.getTransactions(startDatePicker.getValue(), endDatePicker.getValue())) {\n            if (transaction.getTags().contains(tag)) {\n                sum = sum.add(transaction.getAmount(account));\n            }\n        }\n\n        // TODO: exchange rate...\n        if (account.getChildCount() > 0) {\n            for (Account child : account.getChildren()) {\n                sum = sum.add(getSumForTag(tag, child));\n            }\n        }\n\n        return sum;\n    }\n\n    @FXML\n    private void handleSaveAction() {\n        ChartUtilities.saveChart(chartPane);\n    }\n\n    @FXML\n    private void handleCopyToClipboard() {\n        ChartUtilities.copyToClipboard(chartPane);\n    }\n\n    @FXML\n    private void handlePrintAction() {\n        ChartUtilities.printChart(chartPane);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/pdf/PageFormatDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report.pdf;\n\nimport java.awt.print.PageFormat;\nimport java.awt.print.Paper;\nimport java.math.BigDecimal;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.RadioButton;\nimport javafx.stage.Stage;\n\nimport jgnash.report.pdf.Constants;\nimport jgnash.report.pdf.PageSize;\nimport jgnash.report.ui.ReportPrintFactory;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.Nullable;\n\nimport org.apache.commons.math3.util.Precision;\n\n/**\n * Page Format Dialog Controller\n *\n * @author Craig Cavanaugh\n */\npublic class PageFormatDialogController {\n\n    private static final String DEFAULT_MARGIN = \"0.5\";\n\n    private static final String LAST_UNIT = \"lastUnit\";\n\n    private static final float EPSILON = 0.5f;  // round error occur due to saved precision of the page size\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private DecimalTextField leftMarginField;\n\n    @FXML\n    private DecimalTextField rightMarginField;\n\n    @FXML\n    private DecimalTextField topMarginField;\n\n    @FXML\n    private DecimalTextField bottomMarginField;\n\n    @FXML\n    private DecimalTextField widthField;\n\n    @FXML\n    private DecimalTextField heightField;\n\n    @FXML\n    private RadioButton portraitRadioButton;\n\n    @FXML\n    private RadioButton landscapeRadioButton;\n\n    @FXML\n    private ComboBox<PageSize> pageSizeComboBox;\n\n    @FXML\n    private ComboBox<Unit> unitsComboBox;\n\n    @FXML\n    private Button okayButton;\n\n    private final BooleanProperty invalidPageFormatProperty = new SimpleBooleanProperty();\n\n    private final DoubleProperty unitScaleProperty = new SimpleDoubleProperty(Unit.INCHES.scale);\n\n    private Unit lastUnit = Unit.INCHES;\n\n    private PageFormat pageFormat = ReportPrintFactory.getDefaultPage();\n\n    private final DoubleProperty minWidth = new SimpleDoubleProperty();\n\n    // anything less than 2 inches is considered bad\n    private final DoubleProperty minWidthPoints = new SimpleDoubleProperty(Constants.POINTS_PER_INCH * 2);\n\n    private final Preferences preferences = Preferences.userNodeForPackage(PageFormatDialogController.class);\n\n    @FXML\n    void initialize() {\n\n        // setup the defaults\n        unitsComboBox.getItems().setAll(Unit.POINTS, Unit.MM, Unit.INCHES);\n        unitsComboBox.setValue(Unit.INCHES);\n\n        pageSizeComboBox.getItems().setAll(PageSize.values());\n        pageSizeComboBox.setValue(PageSize.LETTER);\n\n        widthField.emptyWhenZeroProperty().setValue(false);\n        heightField.emptyWhenZeroProperty().setValue(false);\n        leftMarginField.emptyWhenZeroProperty().setValue(false);\n        rightMarginField.emptyWhenZeroProperty().setValue(false);\n        topMarginField.emptyWhenZeroProperty().setValue(false);\n        bottomMarginField.emptyWhenZeroProperty().setValue(false);\n\n        // inches\n        widthField.setDecimal(new BigDecimal(\"8.5\"));\n        heightField.setDecimal(new BigDecimal(11));\n\n        portraitRadioButton.setSelected(true);\n\n        // inches\n\n        final BigDecimal margin = new BigDecimal(DEFAULT_MARGIN);\n\n        leftMarginField.setDecimal(margin);\n        rightMarginField.setDecimal(margin);\n        topMarginField.setDecimal(margin);\n        bottomMarginField.setDecimal(margin);\n\n        // install the listeners\n        pageSizeComboBox.valueProperty().addListener((observable, oldValue, newValue) -> handlePageSizeChange());\n        unitsComboBox.valueProperty().addListener((observable, oldValue, newValue) -> handleUnitChange());\n\n        // restore the defaults\n        unitsComboBox.setValue(Unit.values()[preferences.getInt(LAST_UNIT, Unit.INCHES.ordinal())]);\n\n        // bindings to prevent math mischief from occurring\n        minWidth.bind(minWidthPoints.divide(unitScaleProperty));\n\n        invalidPageFormatProperty.bind(widthField.doubleProperty().lessThan(minWidth)\n                .or(heightField.doubleProperty().lessThan(minWidth))\n                .or(rightMarginField.doubleProperty().greaterThan(widthField.doubleProperty()))\n                .or(leftMarginField.doubleProperty().greaterThan(widthField.doubleProperty()))\n                .or(topMarginField.doubleProperty().greaterThan(heightField.doubleProperty()))\n                .or(bottomMarginField.doubleProperty().greaterThan(heightField.doubleProperty()))\n                .or(rightMarginField.doubleProperty().add(leftMarginField.doubleProperty())\n                        .greaterThan(widthField.doubleProperty().subtract(minWidth)))\n                .or(topMarginField.doubleProperty().add(bottomMarginField.doubleProperty())\n                        .greaterThan(heightField.doubleProperty().subtract(minWidth)))\n        );\n\n        okayButton.disableProperty().bind(invalidPageFormatProperty);\n    }\n\n    void setPageFormat(final PageFormat pageFormat) {\n\n        this.pageFormat = pageFormat;\n\n        if (pageFormat.getOrientation() == PageFormat.LANDSCAPE) {\n            landscapeRadioButton.setSelected(true);\n        } else {\n            portraitRadioButton.setSelected(true);\n        }\n\n        // force for correct form orientation\n        pageFormat.setOrientation(PageFormat.PORTRAIT);\n\n        final float width = (float) pageFormat.getWidth();\n        final float height = (float) pageFormat.getHeight();\n        final float imageableX = (float) pageFormat.getImageableX();\n        final float imageableY = (float) pageFormat.getImageableY();\n\n        final float rightMargin = width - (float) pageFormat.getImageableWidth() - imageableX;\n        final float bottomMargin = height - (float) pageFormat.getImageableHeight() - imageableY;\n\n        final PageSize oldPageSize = matchPageSize(width, height);\n\n        if (oldPageSize != null) {\n            pageSizeComboBox.setValue(oldPageSize);\n        }\n\n        // load the fields with the new values\n        final Unit currentUnit = unitsComboBox.getValue();\n\n        handleUnitChange(widthField, width, currentUnit);\n        handleUnitChange(heightField, height, currentUnit);\n        handleUnitChange(leftMarginField, imageableX, currentUnit);\n        handleUnitChange(topMarginField, imageableY, currentUnit);\n        handleUnitChange(rightMarginField, rightMargin, currentUnit);\n        handleUnitChange(bottomMarginField, bottomMargin, currentUnit);\n    }\n\n    private PageSize matchPageSize(final float width, final float height) {\n        for (final PageSize pageSize : PageSize.values()) {\n            if ((compare(width, pageSize.width) && compare(height,pageSize.height))\n                        || (compare(height, pageSize.width) && compare(width, pageSize.height))) {\n                return pageSize;\n            }\n        }\n\n        return null;\n    }\n\n    private boolean compare(float num1, float num2) {\n        return Precision.equals(num1, num2, EPSILON);\n    }\n\n    @Nullable\n    PageFormat getPageFormat() {\n        return pageFormat;\n    }\n\n    private PageFormat generatePageFormat() {\n        final PageFormat pageFormat = new PageFormat();\n        final Unit unit = unitsComboBox.getValue();\n\n        if (portraitRadioButton.isSelected()) {\n            pageFormat.setOrientation(PageFormat.PORTRAIT);\n        } else {\n            pageFormat.setOrientation(PageFormat.LANDSCAPE);\n        }\n\n        double width = widthField.getDecimal().doubleValue() * unit.scale;\n        double height = heightField.getDecimal().doubleValue() * unit.scale;\n        double rightMargin = rightMarginField.getDecimal().doubleValue() * unit.scale;\n        double bottomMargin = bottomMarginField.getDecimal().doubleValue() * unit.scale;\n        double imageableX = leftMarginField.getDecimal().doubleValue() * unit.scale;\n        double imageableY = topMarginField.getDecimal().doubleValue() * unit.scale;\n\n        double imageableWidth = width - imageableX - rightMargin;\n        double imageableHeight = height - imageableY - bottomMargin;\n\n        final Paper paper = pageFormat.getPaper();\n        paper.setSize(width, height);\n        paper.setImageableArea(imageableX, imageableY, imageableWidth, imageableHeight);\n        pageFormat.setPaper(paper);\n\n        return pageFormat;\n    }\n\n    private void handlePageSizeChange() {\n        final PageSize pageSize = pageSizeComboBox.getValue();\n\n        final Unit unit = unitsComboBox.getValue();\n\n        // convert points to selected unit of measure\n        widthField.setDecimal(new BigDecimal(pageSize.width / unit.scale));\n        heightField.setDecimal(new BigDecimal(pageSize.height / unit.scale));\n    }\n\n    /**\n     * Rescales to a new unit of measure\n     */\n    private void handleUnitChange() {\n        final Unit newUnit = unitsComboBox.getValue();\n\n        preferences.putInt(LAST_UNIT, newUnit.ordinal());\n\n        unitScaleProperty.set(newUnit.scale);   // update binding\n\n        handleUnitChange(widthField, lastUnit, newUnit);\n        handleUnitChange(heightField, lastUnit, newUnit);\n        handleUnitChange(leftMarginField, lastUnit, newUnit);\n        handleUnitChange(rightMarginField, lastUnit, newUnit);\n        handleUnitChange(topMarginField, lastUnit, newUnit);\n        handleUnitChange(bottomMarginField, lastUnit, newUnit);\n\n        lastUnit = newUnit;\n    }\n\n    /**\n     * Correctly scales and sets the decimal field\n     *\n     * @param decimalTextField field to set\n     * @param newValue         new value in Points\n     * @param newUnit          unit of measure\n     */\n    private void handleUnitChange(final DecimalTextField decimalTextField, final float newValue, final Unit newUnit) {\n        if (decimalTextField.getDecimal().compareTo(BigDecimal.ZERO) != 0) {\n            JavaFXUtils.runLater(() -> decimalTextField.setDecimal(new BigDecimal(newValue / newUnit.scale)));\n        }\n    }\n\n    private void handleUnitChange(final DecimalTextField decimalTextField, final Unit oldUnit, final Unit newUnit) {\n        if (decimalTextField.getDecimal().compareTo(BigDecimal.ZERO) != 0) {\n            float oldValue = decimalTextField.getDecimal().floatValue() * oldUnit.scale;\n            JavaFXUtils.runLater(() -> decimalTextField.setDecimal(new BigDecimal(oldValue / newUnit.scale)));\n        }\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleCancelAction() {\n        pageFormat = null;\n        handleCloseAction();\n    }\n\n    @FXML\n    private void handleOkAction() {\n        pageFormat = generatePageFormat();\n\n        handleCloseAction();\n    }\n\n    private enum Unit {\n        POINTS(\"Points\", 1),\n        MM(\"Millimeters\", Constants.POINTS_PER_MM),\n        INCHES(\"Inches\", Constants.POINTS_PER_INCH);\n\n        Unit(final String description, final float scale) {\n            this.description = description;\n            this.scale = scale;\n        }\n\n        private final transient String description;\n\n        /**\n         * Number of points per unit of measure\n         */\n        final transient float scale;\n\n        @Override\n        public String toString() {\n            return description;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/pdf/ReportController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report.pdf;\n\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\n\nimport java.util.function.Consumer;\nimport java.util.prefs.Preferences;\n\n/**\n * Required UI interface for a report controller\n *\n * @author Craig Cavanaugh\n */\npublic interface ReportController {\n\n    /**\n     * Preference key\n     */\n    String START_DATE_KEY = \"startDate\";\n\n    /**\n     * Preference key\n     */\n    String END_DATE_KEY = \"endDate\";\n\n    /**\n     * Installs a callback to notify the report viewer that the underlying report has changed itself.\n     *\n     * @param runnable Runnable / callback that should be executed\n     */\n    void setRefreshRunnable(final Runnable runnable);\n\n    /**\n     * Functional return of the report\n     *\n     * @param report report consumer\n     */\n    void getReport(Consumer<Report> report);\n\n    /**\n     * Forces a refresh/rebuild of the report\n     */\n    void refreshReport();\n\n    /**\n     * Generated and returns the {@code AbstractReportTableModel} used for report generation\n     *\n     * @return report model\n     */\n    AbstractReportTableModel createReportModel();\n\n    /**\n     * Returns the default Preference node for the implementing class\n     *\n     * @return Preference node\n     */\n    default Preferences getPreferences() {\n        return Preferences.userNodeForPackage(getClass()).node(getClass().getSimpleName());\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/report/pdf/ReportViewerDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.report.pdf;\n\nimport java.awt.image.BufferedImage;\nimport java.awt.print.PageFormat;\nimport java.io.File;\nimport java.io.IOException;\nimport java.text.DecimalFormat;\nimport java.text.MessageFormat;\nimport java.text.ParseException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.concurrent.Task;\nimport javafx.embed.swing.SwingFXUtils;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.geometry.Insets;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ScrollPane;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory;\nimport javafx.scene.control.ToggleButton;\nimport javafx.scene.effect.DropShadow;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyEvent;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.FileChooser;\nimport javafx.stage.Stage;\n\nimport jgnash.report.pdf.Report;\nimport jgnash.report.poi.Workbook;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.BusyPane;\nimport jgnash.uifx.report.ReportActions;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.FileUtils;\n\nimport org.apache.commons.math3.util.Precision;\n\n/**\n * Viewer controller for PDFBox Reports.\n *\n * @author Craig Cavanaugh\n */\npublic class ReportViewerDialogController {\n\n    private static final String LAST_DIR = \"lastDir\";\n\n    private static final int REPORT_RESOLUTION = 72;\n\n    private static final int UP_SCALING = 2;\n\n    private static final float MIN_ZOOM = 0.5f;\n\n    private static final float MAX_ZOOM = 10f;\n\n    private static final int[] DEFAULT_ZOOMS = {50, 75, 100, 125, 150, 175, 200};\n\n    private static final int DEFAULT_ZOOM_INDEX = 2;\n\n    private static final int PAGE_BORDER = 8;\n\n    private static final int UPDATE_PERIOD = 1500; // update period in milliseconds\n\n    private static final double ZOOM_EPSILON = .001;\n\n    private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1.0);\n\n    private final DecimalFormat zoomDecimalFormat = new DecimalFormat(\"#.#\");\n\n    @FXML\n    private StackPane reportControllerPane;\n\n    @FXML\n    private ToggleButton fitPageButton;\n\n    @FXML\n    private ToggleButton fitHeightButton;\n\n    @FXML\n    private ToggleButton fitWidthButton;\n\n    @FXML\n    private Button zoomInButton;\n\n    @FXML\n    private ComboBox<String> zoomComboBox;\n\n    @FXML\n    private Button zoomOutButton;\n\n    @FXML\n    private VBox pagePane;\n\n    @FXML\n    private ScrollPane scrollPane;\n\n    @FXML\n    private DropShadow dropShadow;\n\n    @FXML\n    private Label statusLabel;\n\n    @FXML\n    private Button firstButton;\n\n    @FXML\n    private Button previousButton;\n\n    @FXML\n    private Button nextButton;\n\n    @FXML\n    private Button lastButton;\n\n    @FXML\n    private Spinner<Double> fontSizeSpinner;\n\n    @FXML\n    private Button reportFormatButton;\n\n    @FXML\n    private Button saveButton;\n\n    @FXML\n    private StackPane stackPane;\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ResourceBundle resources;\n\n    private BusyPane busyPane;\n\n    private final SimpleObjectProperty<Report> report = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<Pane> reportControllerPaneProperty = new SimpleObjectProperty<>();\n\n    private final IntegerProperty pageIndex = new SimpleIntegerProperty();\n\n    private final IntegerProperty pageCount = new SimpleIntegerProperty();\n\n    private ReportController reportController;\n\n    /**\n     * Used to limit report update rates.\n     */\n    private final ScheduledThreadPoolExecutor reportExecutor = new ScheduledThreadPoolExecutor(1,\n            new DefaultDaemonThreadFactory(\"Report View Executor\"), new ThreadPoolExecutor.DiscardPolicy());\n\n    @FXML\n    private void initialize() {\n        busyPane = new BusyPane();\n        stackPane.getChildren().add(busyPane);\n\n        saveButton.disableProperty().bind(report.isNull());\n        reportFormatButton.disableProperty().bind(report.isNull());\n        fontSizeSpinner.disableProperty().bind(report.isNull());\n\n        firstButton.disableProperty().bind(report.isNull().or(pageCount.isEqualTo(0))\n                                                   .or(pageIndex.isEqualTo(0)));\n        previousButton.disableProperty().bind(report.isNull().or(pageCount.isEqualTo(0))\n                                                      .or(pageIndex.isEqualTo(0)));\n\n        nextButton.disableProperty().bind(report.isNull().or(pageCount.isEqualTo(0))\n                                                  .or(pageIndex.isEqualTo(pageCount.subtract(1))));\n        lastButton.disableProperty().bind(report.isNull().or(pageCount.isEqualTo(0))\n                                                  .or(pageIndex.isEqualTo(pageCount.subtract(1))));\n\n        fitPageButton.disableProperty().bind(report.isNull());\n        fitHeightButton.disableProperty().bind(report.isNull());\n        fitWidthButton.disableProperty().bind(report.isNull());\n\n        zoomComboBox.disableProperty().bind(report.isNull());\n        zoomInButton.disableProperty().bind(report.isNull()\n                                                    .or(zoomProperty.greaterThanOrEqualTo(DEFAULT_ZOOMS[DEFAULT_ZOOMS.length - 1] / 100)));\n\n        zoomOutButton.disableProperty().bind(report.isNull()\n                                                     .or(zoomProperty.lessThanOrEqualTo(DEFAULT_ZOOMS[0] / 100)));\n\n        fitPageButton.setSelected(true);\n\n        firstButton.prefHeightProperty().bind(saveButton.heightProperty());\n        previousButton.prefHeightProperty().bind(saveButton.heightProperty());\n        nextButton.prefHeightProperty().bind(saveButton.heightProperty());\n        lastButton.prefHeightProperty().bind(saveButton.heightProperty());\n        zoomInButton.prefHeightProperty().bind(saveButton.heightProperty());\n        zoomOutButton.prefHeightProperty().bind(saveButton.heightProperty());\n        fitHeightButton.prefHeightProperty().bind(saveButton.heightProperty());\n        fitWidthButton.prefHeightProperty().bind(saveButton.heightProperty());\n        fitPageButton.prefHeightProperty().bind(saveButton.heightProperty());\n        fontSizeSpinner.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(5, 15, 7));\n\n        // act when the report property has been set or changed\n        report.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                fontSizeSpinner.valueFactoryProperty().get().setValue((double) newValue.getBaseFontSize());\n            }\n        });\n\n        reportControllerPaneProperty.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                reportControllerPane.getChildren().addAll(newValue);\n            }\n        });\n\n        fontSizeSpinner.valueProperty().addListener((observable, oldValue, newValue) -> {\n            report.get().setBaseFontSize(newValue.floatValue());\n\n            if (reportController != null) {\n                reportController.refreshReport();\n            }\n        });\n\n        pagePane.setSpacing(PAGE_BORDER);\n        pagePane.setPadding(new Insets(PAGE_BORDER));\n        pagePane.setAlignment(Pos.CENTER);\n\n        scrollPane.viewportBoundsProperty().addListener((observable, oldValue, newValue) -> {\n            if (fitWidthButton.isSelected()) {\n                handleFitPageWidthAction();\n            }\n\n            scrollPane.setFitToWidth(pagePane.prefWidth(-1) < newValue.getWidth());\n            scrollPane.setFitToHeight(pagePane.prefHeight(-1) < newValue.getHeight());\n        });\n\n        scrollPane.vvalueProperty().addListener(\n                (ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {\n\n                    final double interval = 1d / pageCount.get();\n                    double low = pageIndex.get() * interval;\n                    double hi = low + interval;\n\n                    int newPageIndex = pageIndex.get();\n\n                    if (hi < newValue.doubleValue() && pageIndex.get() < pageCount.get()) {\n                        while (hi < newValue.doubleValue()) {\n                            newPageIndex++;\n                            hi += interval;\n                        }\n\n                        setPageIndex(newPageIndex); // increase the page index to match the scroll position\n                    } else if (low > newValue.doubleValue() && pageIndex.get() > 0) {\n                        while (low > newValue.doubleValue()) {\n                            newPageIndex--;\n                            low -= interval;\n                        }\n\n                        setPageIndex(newPageIndex); // decrease the page index to match the scroll position\n                    }\n                }\n        );\n\n        for (int zoom : DEFAULT_ZOOMS) {\n            zoomComboBox.getItems().add(zoom + \"%\");\n        }\n        zoomComboBox.getSelectionModel().select(DEFAULT_ZOOM_INDEX);\n        zoomComboBox.addEventHandler(KeyEvent.KEY_PRESSED, (KeyEvent e) -> {\n            if (e.getCode() == KeyCode.ENTER) {\n                handleZoomChangedAction();\n            }\n        });\n\n        setZoomRatio(1);\n\n        // this ensures the report is properly closed when the dialog is closed\n        parent.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                parent.get().getWindow().setOnCloseRequest(event -> {\n                    try {\n                        report.get().close();\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                });\n            }\n        });\n    }\n\n    private void refreshReport() {\n        System.out.println(\"Report was Refreshed!!!!!\");\n        refresh();\n    }\n\n    public <T extends ReportController> T loadReportController(final String fxmlResource) {\n        try {\n            final FXMLLoader fxmlLoader =\n                    new FXMLLoader(ReportActions.class.getResource(fxmlResource), resources);\n\n            reportControllerPaneProperty.setValue(fxmlLoader.load());\n\n            T reportController = fxmlLoader.getController();\n\n            // install handler for refreshing a report\n            reportController.setRefreshRunnable(this::refreshReport);\n\n            // save the reference to the report\n            reportController.getReport(report::set);\n\n            this.reportController = reportController;\n\n            return reportController;\n        } catch (final IOException e) {\n            StaticUIMethods.displayException(e);\n            return null;\n        }\n    }\n\n    /**\n     * Updates the status line and {@code pageIndex property} with the correct page index.\n     *\n     * @param index active page index\n     */\n    private void setPageIndex(final int index) {\n        if (index >= 0 && index < pageCount.get()) {\n            pageIndex.setValue(index);\n            updateStatus(MessageFormat.format(resources.getString(\"Pattern.Pages\"), index + 1, pageCount.get()));\n        } else {\n            updateStatus(\"\");\n        }\n    }\n\n    private void setZoomRatio(final double newZoom) {\n        if (newZoom > 0) {\n            fitPageButton.setSelected(false);\n            fitHeightButton.setSelected(false);\n            fitWidthButton.setSelected(false);\n\n            zoomComboBox.getEditor().setText(zoomDecimalFormat.format(newZoom * 100) + \"%\");\n\n            if (!Precision.equals(zoomProperty.doubleValue(), newZoom, ZOOM_EPSILON)) {\n                zoomProperty.setValue(newZoom);\n            }\n        }\n    }\n\n    private void setActualZoomRatio(final double newZoom) {\n        if (newZoom > 0) {\n            zoomProperty.set(newZoom);\n\n            zoomComboBox.getEditor().setText(zoomDecimalFormat.format(zoomProperty.doubleValue() * 100) + \"%\");\n        }\n    }\n\n    private void refresh() {\n        final List<Node> children = pagePane.getChildren();\n        children.clear();\n        pageCount.set(0);\n\n        reportExecutor.schedule(() -> {\n            if (reportExecutor.getQueue().size() < 1) {   // ignore if we already have one waiting in the queue\n\n                final Task<Void> task = new Task<>() {\n                    @Override\n                    protected Void call() {\n                        updateMessage(resources.getString(\"Message.CompilingReport\"));\n                        updateProgress(-1, Long.MAX_VALUE);\n\n                        if (report.get() != null) {\n\n                            for (int i = 0; i < report.get().getPageCount(); i++) {\n\n                                // report resolution is fixed and the ImageView width and height are adjusted to the zoom value\n                                final BufferedImage bufferedImage = report.get().renderImage(i, REPORT_RESOLUTION * UP_SCALING);\n\n                                JavaFXUtils.runLater(() -> {\n\n                                    final ImageView imageView = new ImageView(SwingFXUtils.toFXImage(bufferedImage, null));\n\n                                    imageView.setEffect(dropShadow);\n\n                                    // bind the width and height to the zoom level\n                                    imageView.fitWidthProperty().bind(zoomProperty.multiply(bufferedImage.getWidth() / UP_SCALING));\n                                    imageView.fitHeightProperty().bind(zoomProperty.multiply(bufferedImage.getHeight() / UP_SCALING));\n\n                                    children.add(imageView);\n\n                                    pageCount.set(pageCount.get() + 1);\n                                });\n                            }\n                        }\n\n                        JavaFXUtils.runLater(() -> setPageIndex(0));\n\n                        return null;\n                    }\n                };\n\n                JavaFXUtils.runLater(() -> {\n                    busyPane.setTask(task);\n                    new Thread(task).start();\n                });\n\n            }\n        }, UPDATE_PERIOD, TimeUnit.MILLISECONDS);\n    }\n\n    private void updateStatus(final String status) {\n        JavaFXUtils.runLater(() -> statusLabel.setText(status));\n    }\n\n    @FXML\n    private void handleSaveAction() {\n        final Preferences pref = Preferences.userNodeForPackage(ReportViewerDialogController.class);\n\n        final FileChooser fileChooser = new FileChooser();\n        fileChooser.setTitle(ResourceUtils.getString(\"Title.SaveFile\"));\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.PDFFiles\") + \" (.pdf)\", \"*.pdf\", \"*.PDF\"),\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.SpreadsheetFiles\") + \" (*.xls, *.xlsx)\",\n                        \"*.xls\", \"*.xlsx\")\n        );\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            pref.put(LAST_DIR, file.getParent());\n\n            final String extension = FileUtils.getFileExtension(file.getAbsolutePath()).toLowerCase(Locale.ROOT);\n\n            switch (extension) {\n                case \"pdf\":\n                    try {\n                        report.get().saveToFile(file.toPath());\n                    } catch (final IOException ex) {\n                        StaticUIMethods.displayException(ex);\n                    }\n                    break;\n                case \"xls\":\n                case \"xlsx\":\n                    final AbstractReportTableModel model = reportController.createReportModel();\n                    Workbook.export(model, file);\n                    break;\n                default:\n                    break;\n            }\n        }\n    }\n\n    private void setPage(final int index) {\n        double contentsHeight = pagePane.getBoundsInLocal().getHeight();\n        double viewportHeight = scrollPane.getViewportBounds().getHeight();\n        ImageView iv = (ImageView) pagePane.getChildren().get(index);\n        scrollPane.setVvalue(iv.getBoundsInParent().getMinY() / (contentsHeight - viewportHeight));\n        setPageIndex(index);\n    }\n\n    @FXML\n    private void handleFormatAction() {\n        final PageFormat oldFormat = report.get().getPageFormat();\n\n        final FXMLUtils.Pair<PageFormatDialogController> pair = FXMLUtils.load(PageFormatDialogController.class.getResource(\"PageFormatDialog.fxml\"),\n                resources.getString(\"Title.PageSetup\"));\n\n        final PageFormatDialogController controller = pair.getController();\n        final Stage stage = pair.getStage();\n\n        StageUtils.addBoundsListener(stage, PageFormatDialogController.class);\n\n        stage.setResizable(false);\n        controller.setPageFormat(oldFormat);\n        stage.showAndWait();\n\n        final PageFormat newFormat = controller.getPageFormat();\n\n        if (newFormat != null) {\n            report.get().setPageFormat(newFormat);\n            reportController.refreshReport();\n        }\n    }\n\n    @FXML\n    private void handleFirstAction() {\n        setPage(0);\n    }\n\n    @FXML\n    private void handlePreviousAction() {\n        setPage(pageIndex.get() - 1);\n    }\n\n    @FXML\n    private void handleNextAction() {\n        setPage(pageIndex.get() + 1);\n    }\n\n    @FXML\n    private void handleLastAction() {\n        setPage(report.get().getPageCount() - 1);\n    }\n\n    private int getZoomRatio() {\n        try {\n            return zoomDecimalFormat.parse(zoomComboBox.getEditor().getText()).intValue();\n        } catch (final ParseException e) {\n            StaticUIMethods.displayException(e);\n        }\n\n        return zoomProperty.intValue();\n    }\n\n    @FXML\n    private void handleZoomInAction() {\n        fitPageButton.setSelected(false);\n        fitHeightButton.setSelected(false);\n        fitWidthButton.setSelected(false);\n\n        int index = Arrays.binarySearch(DEFAULT_ZOOMS, getZoomRatio());\n\n        if (index < 0) {\n            zoomComboBox.getSelectionModel().select(zoomDecimalFormat.format(DEFAULT_ZOOMS[-index - 1]) + \"%\");\n        } else if (index < zoomComboBox.getItems().size() - 1) {\n            zoomComboBox.getSelectionModel().select(zoomDecimalFormat.format(DEFAULT_ZOOMS[index + 1]) + \"%\");\n        }\n    }\n\n    @FXML\n    private void handleZoomOutAction() {\n        fitPageButton.setSelected(false);\n        fitHeightButton.setSelected(false);\n        fitWidthButton.setSelected(false);\n\n        int index = Arrays.binarySearch(DEFAULT_ZOOMS, getZoomRatio());\n\n        if (index > 0) {\n            zoomComboBox.getSelectionModel().select(zoomDecimalFormat.format(DEFAULT_ZOOMS[index - 1]) + \"%\");\n        } else if (index < -1) {\n            zoomComboBox.getSelectionModel().select(zoomDecimalFormat.format(DEFAULT_ZOOMS[-index - DEFAULT_ZOOM_INDEX]) + \"%\");\n        }\n    }\n\n    @FXML\n    private void handleZoomChangedAction() {\n        try {\n            if (zoomComboBox.getValue() != null) { // Can be null when clearSelection() triggers the action\n                float newZoom = zoomDecimalFormat.parse(zoomComboBox.getValue()).floatValue() / 100f;\n\n                if (newZoom < MIN_ZOOM)\n                    newZoom = MIN_ZOOM;\n\n                if (newZoom > MAX_ZOOM)\n                    newZoom = MAX_ZOOM;\n\n                setZoomRatio(newZoom);\n            }\n        } catch (final ParseException e) {\n            StaticUIMethods.displayException(e);\n        }\n    }\n\n    @FXML\n    private void handleFitHeightAction() {\n        final PageFormat pageFormat = report.get().getPageFormat();\n\n        final double heightRatio = (scrollPane.getViewportBounds().getHeight() - (2 * PAGE_BORDER))\n                                           / pageFormat.getHeight();\n\n        final double widthRatio = (scrollPane.getViewportBounds().getWidth() - (2 * PAGE_BORDER))\n                                          / pageFormat.getWidth();\n\n        zoomComboBox.getSelectionModel().clearSelection();\n\n        setActualZoomRatio(Math.min(heightRatio, widthRatio));\n    }\n\n    @FXML\n    private void handleFitPageWidthAction() {\n        zoomComboBox.getSelectionModel().clearSelection();\n\n        setActualZoomRatio((scrollPane.getViewportBounds().getWidth() - (2 * PAGE_BORDER)) /\n                                   report.get().getPageFormat().getWidth());\n    }\n\n    @FXML\n    private void handleActualSizeAction() {\n        zoomComboBox.getSelectionModel().select(DEFAULT_ZOOM_INDEX); // 100% size\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/resource/cursor/CustomCursor.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.resource.cursor;\n\nimport javafx.scene.Cursor;\nimport javafx.scene.ImageCursor;\nimport javafx.scene.image.Image;\n\n/**\n * Custom cursor factory.\n *\n * @author Craig Cavanaugh\n */\npublic final class CustomCursor {\n\n    private static final String ZOOM_IN_CURSOR = \"/jgnash/resource/ZoomInCursor.gif\";\n\n    private static final String ZOOM_OUT_CURSOR = \"/jgnash/resource/ZoomOutCursor.gif\";\n\n    private static Cursor zoomInCursor;\n\n    private static Cursor zoomOutCursor;\n\n    private CustomCursor() {\n        // Utility class\n    }\n\n    public static synchronized Cursor getZoomInCursor() {\n        if (zoomInCursor == null) {\n            zoomInCursor = new ImageCursor(new Image(ZOOM_IN_CURSOR), 4, 4);\n        }\n        return zoomInCursor;\n    }\n\n    public static synchronized Cursor getZoomOutCursor() {\n        if (zoomOutCursor == null) {\n            zoomOutCursor = new ImageCursor(new Image(ZOOM_OUT_CURSOR), 4, 4);\n        }\n        return zoomOutCursor;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/resource/font/MaterialDesignLabel.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.resource.font;\n\nimport java.net.URL;\nimport java.util.Locale;\nimport java.util.logging.Logger;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.binding.StringExpression;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.control.Label;\nimport javafx.scene.paint.Color;\nimport javafx.scene.paint.Paint;\nimport javafx.scene.text.Font;\n\nimport jgnash.resource.util.OS;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.util.EncodeDecode;\nimport jgnash.util.NotNull;\n\n/**\n * Implementation of a Material Design Icons.\n * <p>\n * This scales well with the font sizeProperty changes and is good for use in table cells.\n *\n * @author Craig Cavanaugh\n */\npublic class MaterialDesignLabel extends Label {\n\n    private static final String TTF_PATH = \"/jgnash/fonts/materialdesignicons-webfont.ttf\";\n\n    public static final double DEFAULT_SIZE = 16.0;\n\n    private static final double BASELINE_OFFSET;\n\n    static {\n        if (OS.isSystemWindows()) {\n            BASELINE_OFFSET = -2;\n        } else {    // Linux and OSX\n            BASELINE_OFFSET = -5;\n        }\n\n        final URL url = MaterialDesignLabel.class.getResource(TTF_PATH);\n\n        if (url != null) {\n            Font.loadFont(url.toExternalForm(), ThemeManager.fontScaleProperty().get() * DEFAULT_SIZE);\n        }\n    }\n\n    private final ObjectProperty<Object> glyphName = new SimpleObjectProperty<>();\n\n    private final DoubleProperty sizeProperty = new SimpleDoubleProperty(DEFAULT_SIZE);\n\n    @SuppressWarnings(\"unused\")\n    public MaterialDesignLabel() {\n        this(MDIcon.BUG);\n    }\n\n    public static MaterialDesignLabel fromInteger(final int value, final double size, final long color) {\n        final Color c = Color.web(EncodeDecode.longToColorString(color));\n\n        for (final MDIcon mdIcon : MDIcon.values()) {\n            if (mdIcon.unicode == value) {\n                return new MaterialDesignLabel(mdIcon, size, c);\n            }\n        }\n\n        return new MaterialDesignLabel(MDIcon.BUG);\n    }\n\n    public MaterialDesignLabel(final MDIcon glyphValue) {\n        this(glyphValue, ThemeManager.fontScaleProperty().get() * DEFAULT_SIZE, null);\n    }\n\n    public MaterialDesignLabel(final MDIcon glyphValue, final Double sizeValue) {\n        this(glyphValue, sizeValue, null);\n    }\n\n    public MaterialDesignLabel(final MDIcon glyphValue, final Double sizeValue, final Paint paint) {\n\n        sizeProperty.set(sizeValue);\n\n        final StringExpression iconStyleProperty = Bindings.format(Locale.US,\n                \"-fx-font-family: 'Material Design Icons'; -fx-font-size: %1$.4f; -fx-padding: 0 0 %2$.4f 0\",\n                ThemeManager.fontScaleProperty().multiply(sizeProperty),\n                ThemeManager.fontScaleProperty().multiply(BASELINE_OFFSET));\n\n        setGlyphName(glyphValue);\n\n        styleProperty().bind(iconStyleProperty);\n\n        if (paint != null) {\n            setTextFill(paint);\n        } else {\n            textFillProperty().bind(ThemeManager.controlTextFillProperty());\n        }\n\n        setUserData(glyphValue);    // enum is saved as user data to make lookup easier\n    }\n\n    /**\n     * Unbinds and changes the color of the icon\n     *\n     * @param value new color\n     */\n    public void setColor(final Paint value) {\n        if (textFillProperty().isBound()) { //unbind if needed\n            textFillProperty().unbind();\n        }\n\n        setTextFill(value);\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Object getGlyphName() {\n        return glyphName.get();\n    }\n\n    /**\n     * Set the glyphName to display.\n     *\n     * @param value This can either be the Glyph Name or a unicode character representing the glyph.\n     */\n    public void setGlyphName(final Object value) {\n        try {\n            glyphName.set(value);\n\n            if (value != null) {\n                if (value instanceof Integer) {\n                    setText(new String(new int[]{(int) value}, 0, 1));\n                } else {    //  MDIcon is assumed\n                    setText(new String(new int[]{MDIcon.valueOf(value.toString()).getUnicode()}, 0, 1));\n                }\n            }\n        } catch (final IllegalArgumentException e) {\n            Logger.getLogger(MaterialDesignLabel.class.getName()).warning(e.getLocalizedMessage());\n            setText(Integer.toString(MDIcon.BUG.getUnicode()));\n        }\n    }\n\n    public Double getSize() {\n        return sizeProperty.getValue();\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setSize(final Double value) {\n        sizeProperty.set(value);\n    }\n\n    @SuppressWarnings(\"unused\")\n    public enum MDIcon {\n        ADJUST(0xf01a),\n        AIRPLANE(0xf01d, true),\n        ALBUM(0xf025, true),\n        AMBULANCE(0xf02f, true),\n        ANCHOR(0xf031, true),\n        ANVIL(0xf89a, true),\n        ARM_FLEX(0xf008f, true),\n        ARROW_COLLAPSE_VERTICAL(0xf84c),\n        ARROW_RIGHT_BOLD(0xf733),\n        ARROWS(0xf04c),\n        ARROWS_H(0xf84d),\n        ARROWS_V(0xf84e),\n        ATM(0xfd23, true),\n        BABY(0xf06c, true),\n        BANK(0xf070, true),\n        BANK_PLUS(0xfd8d),\n        BAG_CARRY_ON(0xff58, true),\n        BANDAGE(0xfd8B, true),\n        BASEBALL(0xf851, true),\n        BASKETBALL(0xf805, true),\n        BAT(0xfb3b, true),\n        BED_EMPTY(0xf89f,true),\n        BEACH(0xf092, true),\n        BEER(0xf098, true),\n        BELL(0xf09A, true),\n        BIKE(0xf0a3, true),\n        BLENDER(0xfcc7, true),\n        BOOKMARK(0xf0c0),\n        BUG(0xf0e4),\n        OFFICE_BUILDING(0xf990, true),\n        BULLS_EYE(0xf5dd, true),\n        BUS(0xf0e7, true),\n        BUS_SCHOOL(0xf79e, true),\n        CAKE(0xf0eb, true),\n        CALENDAR(0xf0ed, true),\n        CALENDAR_CHECK(0xf0ef),\n        CALENDAR_PLUS(0xf0f3),\n        CASH(0xf114, true),\n        CASH_MULTIPLE(0xf116),\n        CAMERA(0xf100, true),\n        CAMPFIRE(0xfefa, true),\n        CAR(0xf10b, true),\n        CHART_AREA(0xfeae),\n        CHART_BAR(0xf128),\n        CHART_LINE(0xf12a),\n        CHART_PIE(0xf12b),\n        CHEVRON_LEFT(0xf141),\n        CHEVRON_RIGHT(0xf142),\n        CHECKBOX_BLANK(0xf12e, true),\n        CHILD(0xf2e7, true),\n        CIRCLE(0xf764, true),\n        CIRCLE_OPEN(0xf130, true),\n        CLIPBOARD(0xf147),\n        CLOCK_OUTLINE(0xf150, true),\n        CLOSE_CIRCLE(0xf159),\n        CLOUD_DOWNLOAD(0xf162),\n        COFFEE(0xf176, true),\n        COGS(0xf8d5, true),\n        CONSOLE(0xf18d),\n        CROSS_HAIRS(0xf1a3, true),\n        EDIT(0xfda4),\n        ELLIPSIS_H(0xf1d8),\n        EMOTICON_FROWN(0xff6a, true),\n        EMOTICON_HAPPY(0xf1f5, true),\n        ENVELOPE(0xf1ee), // email\n        EXCLAMATION(0xf0263), // exclamation-thick\n        EXCLAMATION_CIRCLE(0xf028), // alert-circle\n        EXCLAMATION_TRIANGLE(0xf026), // alert\n        EXTERNAL_LINK(0xffe5),  //location-exit\n        EYE(0xf208, true),\n        FEMALE(0xf649, true),\n        FILE(0xf214),\n        FILE_CODE_O(0xf22e),\n        FILE_DOCUMENT_BOX_PLUS(0xfec7),\n        FILE_EXCEL_O(0xf004f),\n        FILE_IMAGE_O(0xf21f),\n        FILTER(0xf232),\n        FLAG(0xf23b, true),\n        FIRE(0xf238, true),\n        FIRE_EXTINGUISHER(0xff0f, true),\n        FLASK(0xf093, true),\n        FOLDER_OPEN(0xf76f),\n        FOOTBALL(0xf25d, true),\n        GAMEPAD_V(0xfed4, true),\n        GIFT_V(0xf2a1, true),\n        HANDSHAKE(0xf0243),\n        HEART_PULSE(0xf5f6, true),\n        HOSPITAL_BOX_OUTLINE(0xf0018, true),\n        HOTEL(0xf2e3, true),\n        INFO(0xf64e),\n        INFO_CIRCLE(0xf2fc),\n        KEY(0xf306),\n        LANGUAGE(0xf0368),\n        LANGUAGE_JAVASCRIPT(0xf31e),\n        LAPTOP(0xf322, true),\n        LEVEL_DOWN(0xf046),   // arrow_down_thick\n        LEVEL_UP(0xf05e), // arrow_up_thick\n        LINK(0xf337),\n        LINK_OFF(0xf338),\n        LIST(0xf279),\n        LOCK(0xf33e, true),\n        MALE(0xf64d, true),\n        MAP(0xf34d, true),\n        MINUS_CIRCLE(0xf376),\n        MOTORCYCLE(0xf37c, true),\n        PAW(0xf3e9, true),\n        PENCIL(0xf3eb, true),\n        PHONE(0xf3f2, true),\n        PLUS(0xf0217),\n        PLUS_CIRCLE(0xf417),\n        PLUS_SQUARE(0xf416),\n        POKER(0xf82f, true),\n        POUND_BOX_OUTLINE(0xf01aa),\n        POWER_PLUG(0xf64a, true),\n        PRINT(0xf42a, true),  //printer\n        QUESTION_CIRCLE(0xf2d7),\n        REFRESH(0xf4e6), // SYNC\n        SAVE(0xf193),\n        SCREW(0xfe56, true),\n        SCHOOL_O(0xf01ab, true),\n        SHIP_WHEEL(0xf832, true),\n        SIGN_OUT(0xf343),\n        SKIP_BACKWARD(0xf4ab),\n        SKIP_FORWARD(0xf4ac),\n        SOCCER(0xf4b8, true),\n        SOLID(0xf68c, true),\n        STEP_BACKWARD(0xf4ae),\n        STEP_FORWARD(0xf4ad),\n        STOP_CIRCLE(0xf666),\n        SUBWAY(0xf6ab,true),\n        SWAP_HORIZONTAL(0xf4e1),\n        TABLE_TENNIS(0xfe4b, true),\n        TABLE_COLUMN_WIDTH(0xf4ef),\n        TAG(0xf4f9),\n        TAGS(0xf4fb),\n        TAXI(0xf4ff, true),\n        TENNIS_BALL(0xf507, true),\n        TEXT_HEIGHT(0xf27f),\n        TOOLS(0xf0086, true),\n        TOOTH(0xf8c2, true),\n        TRASH_O(0xfa79, true),\n        TRIANGLE(0xf536, true),\n        VOLLEYBALL(0xf9b3, true),\n        WRENCH_O(0xfbbc, true);\n\n        private final int unicode;\n\n        private final boolean tag;\n\n        MDIcon(@NotNull final int unicode) {\n            this(unicode, false);\n        }\n\n        MDIcon(@NotNull final int unicode, final boolean tag) {\n            this.unicode = unicode;\n            this.tag = tag;\n        }\n\n        public int getUnicode() {\n            return unicode;\n        }\n\n        /**\n         * Used to filter out Icons not intended to be used as transaction tags\n         * @return true is intended to be used as a tag\n         */\n        public boolean isTag() {\n            return tag;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/skin/BaseColorDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.skin;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ColorPicker;\nimport javafx.stage.WindowEvent;\n\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Controller for selecting the base color for the Fx interface.\n *\n * @author Craig Cavanaugh\n */\npublic class BaseColorDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ColorPicker accentColorPicker;\n\n    @FXML\n    private ColorPicker focusColorPicker;\n\n    @FXML\n    private ColorPicker colorPicker;\n\n    @FXML\n    void initialize() {\n        accentColorPicker.setValue(ThemeManager.accentColorProperty().getValue());\n        colorPicker.setValue(ThemeManager.baseColorProperty().getValue());\n        focusColorPicker.setValue(ThemeManager.focusColorProperty().getValue());\n\n        ThemeManager.accentColorProperty().bind(accentColorPicker.valueProperty());\n        ThemeManager.baseColorProperty().bind(colorPicker.valueProperty());\n        ThemeManager.focusColorProperty().bind(focusColorPicker.valueProperty());\n\n        // Unbind  when the dialog closes\n        parent.addListener((observable, oldValue, scene) -> {\n            if (scene != null) {\n                scene.windowProperty().addListener((observable1, oldValue1, window)\n                        -> window.addEventHandler(WindowEvent.WINDOW_HIDING, event -> {\n                    ThemeManager.accentColorProperty().unbind();\n                    ThemeManager.baseColorProperty().unbind();\n                    ThemeManager.focusColorProperty().unbind();\n                }));\n            }\n        });\n    }\n\n    @FXML\n    private void handleDefaultColorAction() {\n        colorPicker.setValue(ThemeManager.getDefaultColor(ThemeManager.getCurrentTheme(), ThemeManager.BASE));\n    }\n\n    @FXML\n    private void handleDefaultAccentColorAction() {\n        accentColorPicker.setValue(ThemeManager.getDefaultColor(ThemeManager.getCurrentTheme(), ThemeManager.ACCENT));\n    }\n\n    @FXML\n    private void handleDefaultFocusColorAction() {\n        focusColorPicker.setValue(ThemeManager.getDefaultColor(ThemeManager.getCurrentTheme(), ThemeManager.FOCUS));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/skin/FontSizeDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.skin;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Slider;\nimport javafx.stage.WindowEvent;\nimport javafx.util.StringConverter;\n\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Font size dialog controller.\n *\n * @author Craig Cavanaugh\n */\npublic class FontSizeDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Slider slider;\n\n    @FXML\n    void initialize() {\n        // Match the current value so it's not reset\n        slider.setValue(ThemeManager.fontScaleProperty().get() * 100);\n\n        slider.labelFormatterProperty().set(new DoubleStringConverter());\n\n        // Bind the font size to the slider\n        ThemeManager.fontScaleProperty().bind(slider.valueProperty().divide(100));\n\n        // Unbind the font size when the dialog closes\n        parent.addListener((observable, oldValue, scene) -> {\n            if (scene != null) {\n                scene.windowProperty().addListener((observable1, oldValue1, window)\n                        -> window.addEventHandler(WindowEvent.WINDOW_HIDING, event ->\n                        ThemeManager.fontScaleProperty().unbind()));\n            }\n        });\n    }\n\n    private static class DoubleStringConverter extends StringConverter<Double> {\n        @Override\n        public String toString(Double object) {\n            return String.format(\"%.1f%%\", object);\n        }\n\n        @Override\n        public Double fromString(String string) {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/skin/StyleClass.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.skin;\n\n/**\n * Global Styles.\n *\n * @author Craig Cavanaugh\n */\npublic class StyleClass {\n\n    private StyleClass() {\n        // utility class\n    }\n\n    public static final String HIDDEN_COLUMN_HEADER = \"hidden-column-header\";\n\n    public static final String POP_OVER_BUTTON = \"pop-over-button\";\n\n    public static final String HIDDEN_ROW_FOCUS = \"hidden-row-focus\";\n\n    public static final String LIST_TITLE_STYLE = \"list-title\";\n\n    public static final String LIST_BUTTON_STYLE = \"list-button\";\n\n    public static final String DISABLED_CELL_ID = \"disabled-cell\";\n\n    public static final String ENABLED_CELL_ID = \"enabled-cell\";\n\n    public static final String NORMAL_NEGATIVE_CELL_ID = \"normal-negative-label\";\n\n    public static final String TODAY_NORMAL_NEGATIVE_CELL_ID = \"today-normal-negative-label\";\n\n    public static final String NORMAL_CELL_ID = \"normal-label\";\n\n    public static final String TODAY_NORMAL_CELL_ID = \"today-normal-label\";\n\n    public static final String BOLD_LABEL_ID = \"bold-label\";\n\n    public static final String TODAY_BOLD_LABEL_ID = \"today-bold-label\";\n\n    public static final String BOLD_NEGATIVE_LABEL_ID = \"bold-negative-label\";\n\n    public static final String TODAY_BOLD_NEGATIVE_LABEL_ID = \"today-bold-negative-label\";\n\n    public static final String ITALIC_CELL_ID = \"italic-label\";\n\n    public static final String ITALIC_NEGATIVE_CELL_ID = \"italic-negative-label\";\n\n    public static final String HIDE_HORIZONTAL_CSS = \"jgnash/skin/tableHideHorizontalScrollBar.css\";\n\n    public static final String HIDE_VERTICAL_CSS = \"jgnash/skin/tableHideVerticalScrollBar.css\";\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/skin/ThemeManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.skin;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.prefs.Preferences;\n\nimport javafx.application.Application;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.binding.StringExpression;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.property.StringProperty;\nimport javafx.beans.value.ObservableValue;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.scene.Group;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.RadioMenuItem;\nimport javafx.scene.control.ToggleGroup;\nimport javafx.scene.paint.Color;\nimport javafx.scene.paint.Paint;\nimport javafx.scene.text.Text;\n\nimport jgnash.resource.util.OS;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Theme manager.\n *\n * @author Craig Cavanaugh\n */\npublic class ThemeManager {\n\n    /**\n     * Default style sheet.\n     */\n    private static final String DEFAULT_CSS = \"jgnash/skin/default.css\";\n\n    private static final String USER_STYLE = \"userStyle\";\n\n    private static final Preferences preferences;\n\n    private static final String LAST = \"last\";\n\n    private static final String FONT_SCALE = \"fontScale\";\n\n    private static final String ACCENT_COLOR = \"accentColor\";\n\n    private static final String BASE_COLOR = \"baseColor\";\n\n    private static final String FOCUS_COLOR = \"focusColor\";\n\n    private static Menu themesMenu;\n\n    private static final ToggleGroup toggleGroup = new ToggleGroup();\n\n    private static final ThemeHandler themeHandler = new ThemeHandler();\n\n    private static final byte CASPIAN = 0;\n\n    private static final byte MODENA = 1;\n\n    static final byte ACCENT = 0;\n\n    static final byte BASE = 1;\n\n    static final byte FOCUS = 2;\n\n    private static final String DEFAULT_CASPIAN_ACCENT_COLOR = \"#0093ff\";\n\n    private static final String DEFAULT_MODENA_ACCENT_COLOR = \"#0096c9\";\n\n    private static final String DEFAULT_CASPIAN_BASE_COLOR = \"#d0d0d0\";\n\n    private static final String DEFAULT_MODENA_BASE_COLOR = \"#ececec\";\n\n    private static final String DEFAULT_CASPIAN_FOCUS_COLOR = \"#0093ff\";\n\n    private static final String DEFAULT_MODENA_FOCUS_COLOR = \"#039ED3\";\n\n    private static final String[][] KNOWN_THEMES = {\n            {\"Modena\", Application.STYLESHEET_MODENA},\n            {\"Caspian\", Application.STYLESHEET_CASPIAN},\n    };\n\n    private static final String[][] DEFAULT_COLORS = {\n            {DEFAULT_CASPIAN_ACCENT_COLOR, DEFAULT_CASPIAN_BASE_COLOR, DEFAULT_CASPIAN_FOCUS_COLOR},\n            {DEFAULT_MODENA_ACCENT_COLOR, DEFAULT_MODENA_BASE_COLOR, DEFAULT_MODENA_FOCUS_COLOR}\n    };\n\n    private static final DoubleProperty fontScale = new SimpleDoubleProperty(1);\n\n    private static final SimpleObjectProperty<Color> accentColor = new SimpleObjectProperty<>();\n\n    private static final SimpleObjectProperty<Color> baseColor = new SimpleObjectProperty<>();\n\n    private static final SimpleObjectProperty<Color> focusColor = new SimpleObjectProperty<>();\n\n    private static final SimpleObjectProperty<Paint> controlTextFill = new SimpleObjectProperty<>(Color.BLACK);\n\n    private static final StringExpression styleProperty;\n\n    private static final double WINDOWS_DEFAULT = 0.95;\n\n    private static final double OPACITY_FACTOR = 0.1334;\n\n    static {\n        preferences = Preferences.userNodeForPackage(ThemeManager.class);\n\n        final StringProperty _accentColor = new SimpleStringProperty();\n        final StringProperty _baseColor = new SimpleStringProperty();\n        final StringProperty _focusColor = new SimpleStringProperty();\n        final StringProperty _faintFocusColor = new SimpleStringProperty();\n\n        // restore the old value, default to a smaller value for Windows OS\n        fontScale.set(preferences.getDouble(FONT_SCALE, OS.isSystemWindows() ? WINDOWS_DEFAULT : 1));\n\n        // Save the value when it changes\n        fontScale.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                preferences.putDouble(FONT_SCALE, newValue.doubleValue());\n            }\n        });\n\n        accentColor.set(Color.web(preferences.get(ACCENT_COLOR, DEFAULT_MODENA_ACCENT_COLOR)));\n        baseColor.set(Color.web(preferences.get(BASE_COLOR, DEFAULT_MODENA_BASE_COLOR)));\n        focusColor.set(Color.web(preferences.get(FOCUS_COLOR, DEFAULT_MODENA_FOCUS_COLOR)));\n\n        // restore the old base color value\n        switch (preferences.get(LAST, Application.STYLESHEET_MODENA)) {\n            case Application.STYLESHEET_CASPIAN:\n                accentColor.set(Color.web(preferences.get(ACCENT_COLOR, DEFAULT_CASPIAN_ACCENT_COLOR)));\n                baseColor.set(Color.web(preferences.get(BASE_COLOR, DEFAULT_CASPIAN_BASE_COLOR)));\n                focusColor.set(Color.web(preferences.get(FOCUS_COLOR, DEFAULT_CASPIAN_FOCUS_COLOR)));\n                break;\n            case Application.STYLESHEET_MODENA:\n            default:\n                accentColor.set(Color.web(preferences.get(ACCENT_COLOR, DEFAULT_MODENA_ACCENT_COLOR)));\n                baseColor.set(Color.web(preferences.get(BASE_COLOR, DEFAULT_MODENA_BASE_COLOR)));\n                focusColor.set(Color.web(preferences.get(FOCUS_COLOR, DEFAULT_MODENA_FOCUS_COLOR)));\n        }\n\n        _accentColor.set(colorToHex(accentColor.getValue()));\n        _baseColor.set(colorToHex(baseColor.getValue()));\n        _focusColor.set(colorToHex(focusColor.getValue()));\n        _faintFocusColor.set(colorToHex(Color.web(colorToHex(focusColor.getValue()), OPACITY_FACTOR)));\n\n        accentColor.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                preferences.put(ACCENT_COLOR, colorToHex(newValue));\n                _accentColor.set(colorToHex(newValue));\n            }\n        });\n\n        // Save the value when it changes\n        baseColor.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                preferences.put(BASE_COLOR, colorToHex(newValue));\n                _baseColor.set(colorToHex(newValue));\n\n                controlTextFill.set(getBaseTextColor());\n            }\n        });\n\n        focusColor.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                preferences.put(FOCUS_COLOR, colorToHex(newValue));\n                _focusColor.set(colorToHex(newValue));\n                _faintFocusColor.set(colorToHex(Color.web(colorToHex(focusColor.getValue()), OPACITY_FACTOR)));\n            }\n        });\n\n        // Create the binding format for the style / font size\n        styleProperty = Bindings.format(Locale.US, \"-fx-font-size: %1$.6fem; -fx-base: %2$s; -fx-focus-color: %3$s; \" +\n                        \"-fx-faint-focus-color: %4$s; -fx-accent: %5$s; -fx-selection-bar: %3$s\",\n                fontScale, _baseColor, _focusColor, _faintFocusColor,\n                _accentColor);\n    }\n\n    private ThemeManager() {\n        // Utility class\n    }\n\n    public static void applyStyleSheets(final Scene scene) {\n        final String userTheme = preferences.get(USER_STYLE, null);\n\n        if (userTheme != null && !userTheme.isBlank()) {\n            scene.getStylesheets().addAll(ThemeManager.DEFAULT_CSS, userTheme);\n        } else {\n            scene.getStylesheets().addAll(ThemeManager.DEFAULT_CSS);\n        }\n    }\n\n    public static void applyStyleSheets(final Parent parent) {\n        final String userTheme = preferences.get(USER_STYLE, null);\n\n        if (userTheme != null && !userTheme.isBlank()) {\n            parent.getStylesheets().addAll(ThemeManager.DEFAULT_CSS, userTheme);\n        } else {\n            parent.getStylesheets().addAll(ThemeManager.DEFAULT_CSS);\n        }\n    }\n\n    static Color getDefaultColor(final String theme, final byte colorIndex) {\n        return Color.web(DEFAULT_COLORS[themeToIndex(theme)][colorIndex]);\n    }\n\n    private static byte themeToIndex(final String theme) {\n        switch (theme) {\n            case Application.STYLESHEET_CASPIAN:\n                return CASPIAN;\n            case Application.STYLESHEET_MODENA:\n            default:\n                return MODENA;\n        }\n    }\n\n    public static void addKnownThemes(@NotNull final Menu menu) {\n        Objects.requireNonNull(menu);\n\n        themesMenu = menu;\n\n        for (final String[] theme : KNOWN_THEMES) {\n            final RadioMenuItem radioMenuItem = new RadioMenuItem(theme[0]);\n            radioMenuItem.setUserData(theme[1]);\n            radioMenuItem.setOnAction(themeHandler);\n            radioMenuItem.setToggleGroup(toggleGroup);\n\n            themesMenu.getItems().add(radioMenuItem);\n        }\n\n        JavaFXUtils.runLater(ThemeManager::syncRadioMenuItem);\n    }\n\n    private static void syncRadioMenuItem() {\n        final String last = preferences.get(LAST, Application.STYLESHEET_MODENA);\n\n        for (final MenuItem menuItem : themesMenu.getItems()) {\n            if (menuItem.getUserData() != null && menuItem.getUserData().equals(last)) {\n                ((RadioMenuItem) menuItem).setSelected(true);\n                break;\n            }\n        }\n    }\n\n    public static void restoreLastUsedTheme() {\n        Application.setUserAgentStylesheet(preferences.get(LAST, Application.STYLESHEET_MODENA));\n\n        controlTextFill.setValue(getBaseTextColor());   // force an update after the stylesheet has been applied\n    }\n\n    public static boolean setUserStyle(@Nullable Path path) {\n\n        boolean result = false;\n\n        if (path != null && Files.exists(path)) {\n            final String newValue = \"file:///\" + path.toString().replace(\"\\\\\", \"/\");\n\n            if (!preferences.get(USER_STYLE, \"\").equals(newValue)) {\n                preferences.put(USER_STYLE, \"file:///\" + path.toString().replace(\"\\\\\", \"/\"));\n\n                applyStyleSheets(MainView.getPrimaryStage().getScene());    // apply style sheet\n\n                result = true;\n            }\n        } else {\n            if (preferences.get(USER_STYLE, null) != null) {\n                preferences.remove(USER_STYLE);\n                result = true;\n            }\n        }\n\n        return result;\n    }\n\n    public static ObservableValue<String> styleProperty() {\n        return styleProperty;\n    }\n\n    /**\n     * Font scale property.  Always use a weak listener to prevent leaks\n     *\n     * @return current scale factor\n     */\n    public static DoubleProperty fontScaleProperty() {\n        return fontScale;\n    }\n\n    static SimpleObjectProperty<Color> baseColorProperty() {\n        return baseColor;\n    }\n\n    static SimpleObjectProperty<Color> focusColorProperty() {\n        return focusColor;\n    }\n\n    static SimpleObjectProperty<Color> accentColorProperty() {\n        return accentColor;\n    }\n\n    public static SimpleObjectProperty<Paint> controlTextFillProperty() {\n        return controlTextFill;\n    }\n\n    static String getCurrentTheme() {\n        return preferences.get(LAST, Application.STYLESHEET_MODENA);\n    }\n\n    /**\n     * Utility method to discover the {@code Paint} used for {@code Button} text.\n     *\n     * @return Base Paint used for Buttons\n     */\n    private static Paint getBaseTextColor() {\n        final Button button = new Button(BASE_COLOR);\n        final Scene scene = new Scene(new Group(button));\n        scene.getRoot().styleProperty().setValue(styleProperty().getValue());\n        button.applyCss();\n        return button.getTextFill();\n    }\n\n    /**\n     * Utility method to discover the base font size in pixels.\n     *\n     * @return font size in pixels\n     */\n    public static double getBaseTextHeight() {\n        final Text text = new Text();\n        final Scene scene = new Scene(new Group(text));\n        scene.getRoot().styleProperty().setValue(styleProperty().getValue());\n        text.applyCss();\n\n        return Math.ceil(text.getLayoutBounds().getHeight());\n    }\n\n    private static class ThemeHandler implements EventHandler<ActionEvent> {\n        @Override\n        public void handle(final ActionEvent event) {\n            final MenuItem menuItem = (MenuItem) event.getSource();\n\n            Application.setUserAgentStylesheet(menuItem.getUserData().toString());\n\n            preferences.put(LAST, menuItem.getUserData().toString());\n\n            final String current = preferences.get(LAST, null);\n\n            if (current != null && !current.equals(menuItem.getUserData().toString())) {\n                Application.setUserAgentStylesheet(menuItem.getUserData().toString());\n            }\n        }\n    }\n\n    private static String colorToHex(final Color color) {\n        return String.format(\"#%02X%02X%02X%02X\",\n                (int) (color.getRed() * 255),\n                (int) (color.getGreen() * 255),\n                (int) (color.getBlue() * 255),\n                (int) (color.getOpacity() * 255));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/tasks/BootEngineTask.java",
    "content": "package jgnash.uifx.tasks;\n\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ResourceBundle;\nimport java.util.logging.Logger;\n\nimport javafx.concurrent.Task;\n\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.FileUtils;\n\n/**\n * Boots the engine with a local file or connection to a remote server.\n *\n * @author Craig Cavanaugh\n */\npublic class BootEngineTask extends Task<String> {\t\t\n\n    public static final int FORCED_DELAY = 1500;\n\n    private static final int INDETERMINATE = -1;\n\n    private final boolean remote;\n    private final String localFile;\n    private final char[] password;\n    private final String serverName;\n    private final int port;\n\n    private BootEngineTask(final String localFile, final char[] password, final boolean remote,\n                           final String serverName, final int port) {\n        this.localFile = localFile;\n        this.password = password;\n        this.remote = remote;\n        this.serverName = serverName;\n        this.port = port;\n    }\n\n    public static void openLast() {\n        final BootEngineTask bootTask;\n\n        // must be a remote connection without use of a password\n        if (EngineFactory.getLastRemote() && !EngineFactory.usedPassword()) {\n            bootTask = new BootEngineTask(null, EngineFactory.EMPTY_PASSWORD, true,\n                    EngineFactory.getLastHost(), EngineFactory.getLastPort());\n        } else {\n            bootTask = new BootEngineTask(EngineFactory.getLastDatabase(), EngineFactory.EMPTY_PASSWORD, false,\n                    null, 0);\n        }\n\n        new Thread(bootTask).start();\n\n        StaticUIMethods.displayTaskProgress(bootTask);\n    }\n\n    public static void initiateBoot(final String localFile, final char[] password, final boolean remote,\n                                    final String serverName, final int port) {\n        final BootEngineTask bootTask = new BootEngineTask(localFile, password, remote, serverName, port);\n\n        new Thread(bootTask).start();\n\n        StaticUIMethods.displayTaskProgress(bootTask);\n    }\n\n    @Override\n    protected String call() throws Exception {\n\n        ResourceBundle resources = ResourceUtils.getBundle();\n\n        updateMessage(resources.getString(\"Message.LoadingFile\"));\n        updateProgress(INDETERMINATE, Long.MAX_VALUE);\n\n        // Close an open files or connections first\n        if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {\n            EngineFactory.closeEngine(EngineFactory.DEFAULT);\n        }\n                \n        final String lockedMessage = resources.getString(\"Message.FileIsLocked\") + \": \" + localFile;\n        String message = resources.getString(\"Message.FileLoadComplete\");\n\n        if (remote) {\n            try {\n                EngineFactory.bootClientEngine(serverName, port, password, EngineFactory.DEFAULT);\n            } catch (final Exception exception) {\n                JavaFXUtils.runLater(() -> StaticUIMethods.displayException(exception));\n            }\n        } else {\n            if (!Files.exists(Paths.get(localFile))) {\n                message = resources.getString(\"Message.Error.FileNotFound\") + \": \" + localFile;\n                updateMessage(message);\n            } else if (FileUtils.isFileLocked(localFile)) {\n                final Runnable UIRunnable = () -> StaticUIMethods.displayError(lockedMessage);\n\n                if (FileUtils.isLockFileStale(localFile)) {\n                    // try to remove the lock file first\n                    Logger.getLogger(BootEngineTask.class.getName()).info(\"Attempting to remove stale file lock\");\n\n                    if (FileUtils.deleteLockFile(localFile)) {\n                        return call();  // recursive call to rerun the task and load the file to keep code simple\n                    }\n\n                    JavaFXUtils.runLater(UIRunnable);\n                } else {\n                    message = lockedMessage;\n                    updateMessage(message);\n                    JavaFXUtils.runLater(UIRunnable);\n                }\n            } else  {\n                EngineFactory.bootLocalEngine(localFile, EngineFactory.DEFAULT, password);\n                updateMessage(resources.getString(\"Message.FileLoadComplete\"));\n                Thread.sleep(FORCED_DELAY); // force delay for better visual feedback\n            }\n        }\n\n        return message;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/tasks/CloseFileTask.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.tasks;\n\nimport java.util.ResourceBundle;\n\nimport javafx.application.Platform;\nimport javafx.concurrent.Task;\n\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Task to close a file while updating progress.\n *\n * @author Craig Cavanaugh\n */\npublic class CloseFileTask extends Task<String> {\n\n    private static final int FORCED_DELAY = 1500;\n\n    private static final int INDETERMINATE = -1;\n\n    public static void initiateFileClose() {\n        final CloseFileTask closeFileTask = new CloseFileTask();\n\n        final Thread thread = new Thread(closeFileTask);\n        thread.setDaemon(true);\n        thread.start();\n\n        StaticUIMethods.displayTaskProgress(closeFileTask);\n    }\n\n    public static void initiateShutdown() {\n        final CloseFileTask closeFileTask = new CloseFileTask();\n        closeFileTask.setOnSucceeded(event -> Platform.exit());\n\n        final Thread thread = new Thread(closeFileTask);\n        thread.setDaemon(true);\n        thread.start();\n\n        StaticUIMethods.displayTaskProgress(closeFileTask);\n    }\n\n    @Override\n    protected String call() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        try {\n            updateMessage(resources.getString(\"Message.SavingFile\"));\n            updateProgress(INDETERMINATE, Long.MAX_VALUE);\n\n            final Thread thread = new Thread(() -> {\n                try {\n                    Thread.sleep(FORCED_DELAY); // lets the UI catch up\n                } catch (final InterruptedException exception) {\n                    JavaFXUtils.runLater(() -> StaticUIMethods.displayException(exception));\n                }\n                EngineFactory.closeEngine(EngineFactory.DEFAULT);\n            });\n\n            thread.start();\n            thread.join();\n\n            updateMessage(resources.getString(\"Message.FileSaveComplete\"));\n            Thread.sleep(FORCED_DELAY);\n        } catch (final Exception exception) {\n            JavaFXUtils.runLater(() -> StaticUIMethods.displayException(exception));\n        }\n\n        return resources.getString(\"Message.FileSaveComplete\");\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/tasks/PackDatabaseTask.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.tasks;\n\nimport java.text.NumberFormat;\nimport java.util.ResourceBundle;\n\nimport javafx.concurrent.Task;\n\nimport jgnash.engine.DataStore;\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.FileUtils;\n\n/**\n * PackDatabase Task.\n *\n * @author Craig Cavanaugh\n */\npublic class PackDatabaseTask extends Task<Void> {\n\n    private static final int FORCED_DELAY = 1500;\n\n    private static final int INDETERMINATE = -1;\n\n    private static final String MESSAGE_PLEASE_WAIT = \"Message.PleaseWait\";\n\n    private final String file;\n\n    private final char[] password;\n\n    private PackDatabaseTask(final String file, final char[] password) {\n        this.file = file;\n        this.password = password;\n    }\n\n    public static void start(final String fileName, final char[] password) {\n        if (fileName != null) {\n            final PackDatabaseTask saveAsTask = new PackDatabaseTask(fileName, password);\n            new Thread(saveAsTask).start();\n            StaticUIMethods.displayTaskProgress(saveAsTask);\n        }\n    }\n\n    @Override\n    protected Void call() {\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        boolean fileLoaded = false;\n\n        try {\n            updateMessage(resources.getString(MESSAGE_PLEASE_WAIT));\n            updateProgress(INDETERMINATE, Long.MAX_VALUE);\n\n            // Close an active database\n            if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {\n\n                // is the file we want to pack already open?\n                if (EngineFactory.getActiveDatabase().equals(file)) {\n                    fileLoaded = true;\n                }\n\n                updateMessage(resources.getString(MESSAGE_PLEASE_WAIT) + \"\\n\"\n                        + resources.getString(\"Message.ClosingFile\"));\n\n                EngineFactory.closeEngine(EngineFactory.DEFAULT);\n            }\n\n            updateMessage(resources.getString(MESSAGE_PLEASE_WAIT) + \"\\n\"\n                    + resources.getString(\"Message.PackingFile\"));\n\n            final DataStore dataStore = EngineFactory.getDataStoreByType(file).getDataStore();\n\n            final String newFile = FileUtils.stripFileExtension(file) + \"-pack\" + dataStore.getFileExt();\n\n            final String oldFile = FileUtils.stripFileExtension(file) + \"-old\" + dataStore.getFileExt();\n\n            final NumberFormat percentFormat = NumericFormats.getPercentageFormat();\n\n            EngineFactory.saveAs(file, newFile, password, value\n                    -> updateMessage(resources.getString(MESSAGE_PLEASE_WAIT) + \"\\n\"\n                    + resources.getString(\"Message.PackingFile\") + \"\\n\"\n                    + percentFormat.format(value)));\n\n            dataStore.rename(file, oldFile);    // rename the old\n            dataStore.rename(newFile, file);    // rename the new to the original\n\n            if (fileLoaded) {   // boot the compressed file\n                EngineFactory.bootLocalEngine(file, EngineFactory.DEFAULT, password);\n            }\n\n            updateProgress(1, 1);\n            updateMessage(resources.getString(\"Message.PackingFileComplete\"));\n            Thread.sleep(FORCED_DELAY * 2L);\n        } catch (final Exception exception) {\n            JavaFXUtils.runLater(() -> StaticUIMethods.displayException(exception));\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/tasks/SaveAsTask.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.tasks;\n\nimport java.io.File;\nimport java.text.NumberFormat;\nimport java.util.ResourceBundle;\n\nimport javafx.concurrent.Task;\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.FileChooserFactory;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * Save File As Task.\n *\n * @author Craig Cavanaugh\n */\npublic class SaveAsTask extends Task<Void> {\n\n    private static final int FORCED_DELAY = 1500;\n    private static final int INDETERMINATE = -1;\n\n    private final File newFile;\n\n    private SaveAsTask(final File file) {\n        this.newFile = file;\n    }\n\n    public static void start() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n        final File current = new File(EngineFactory.getActiveDatabase());\n\n        final FileChooser fileChooser = FileChooserFactory.getDataStoreChooser();\n        fileChooser.setInitialDirectory(current.getParentFile());\n        fileChooser.setTitle(resources.getString(\"Title.SaveAs\"));\n\n        final File newFile = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n        if (newFile != null) {\n            final SaveAsTask saveAsTask = new SaveAsTask(newFile);\n            new Thread(saveAsTask).start();\n            StaticUIMethods.displayTaskProgress(saveAsTask);\n        }\n    }\n\n    @Override\n    protected Void call() {\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final NumberFormat percentFormat = NumericFormats.getPercentageFormat();\n\n        try {\n            updateMessage(resources.getString(\"Message.PleaseWait\"));\n            updateProgress(INDETERMINATE, Long.MAX_VALUE);\n\n            EngineFactory.saveAs(newFile.getAbsolutePath(), value ->\n                    updateMessage(resources.getString(\"Message.PleaseWait\") + \"\\n\" + percentFormat.format(value)));\n\n            updateProgress(1, 1);\n            updateMessage(resources.getString(\"Message.FileSaveComplete\"));\n            Thread.sleep(FORCED_DELAY * 2);\n        } catch (final Exception exception) {\n            JavaFXUtils.runLater(() -> StaticUIMethods.displayException(exception));\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/AccountTypeFilter.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.InvalidationListener;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\n\n/**\n * Account Type Filter class.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountTypeFilter {\n\n    private static final String HIDDEN_VISIBLE = \"HiddenVisible\";\n    private static final String EXPENSE_VISIBLE = \"ExpenseVisible\";\n    private static final String INCOME_VISIBLE = \"IncomeVisible\";\n    private static final String ACCOUNT_VISIBLE = \"AccountVisible\";\n\n    private final BooleanProperty accountTypesVisible = new SimpleBooleanProperty(true);\n    private final BooleanProperty expenseTypesVisible = new SimpleBooleanProperty(true);\n    private final BooleanProperty incomeTypesVisible = new SimpleBooleanProperty(true);\n    private final BooleanProperty hiddenTypesVisible = new SimpleBooleanProperty(true);\n\n    private final Preferences preferences;\n\n    public AccountTypeFilter(final Preferences preferences) {\n        this.preferences = preferences;\n\n        accountTypesVisible.set(preferences.getBoolean(ACCOUNT_VISIBLE, true));\n        expenseTypesVisible.set(preferences.getBoolean(EXPENSE_VISIBLE, true));\n        incomeTypesVisible.set(preferences.getBoolean(INCOME_VISIBLE, true));\n        hiddenTypesVisible.set(preferences.getBoolean(HIDDEN_VISIBLE, true));\n\n        // Add change listeners to write preferences\n        accountTypesVisible.addListener((observable, oldValue, newValue) -> setFilter(observable.getValue(), ACCOUNT_VISIBLE));\n        incomeTypesVisible.addListener((observable, oldValue, newValue) -> setFilter(observable.getValue(), INCOME_VISIBLE));\n        expenseTypesVisible.addListener((observable, oldValue, newValue) -> setFilter(observable.getValue(), EXPENSE_VISIBLE));\n        hiddenTypesVisible.addListener((observable, oldValue, newValue) -> setFilter(observable.getValue(), HIDDEN_VISIBLE));\n    }\n\n    public BooleanProperty getAccountTypesVisibleProperty() {\n        return accountTypesVisible;\n    }\n\n    public BooleanProperty getIncomeTypesVisibleProperty() {\n        return incomeTypesVisible;\n    }\n\n    public BooleanProperty getExpenseTypesVisibleProperty() {\n        return expenseTypesVisible;\n    }\n\n    public BooleanProperty getHiddenTypesVisibleProperty() {\n        return hiddenTypesVisible;\n    }\n\n    private void setFilter(final boolean visible, final String propertyKey) {\n        preferences.putBoolean(propertyKey, visible);\n    }\n\n    /**\n     * Determines if an account is visible.\n     *\n     * @param a account to check for visibility\n     * @return true is account should be displayed\n     */\n    public boolean isAccountVisible(final Account a) {\n        final AccountType type = a.getAccountType();\n\n        if (type == AccountType.INCOME && incomeTypesVisible.get()) {\n            return a.isVisible() || hiddenTypesVisible.get();\n        } else if (type == AccountType.EXPENSE && expenseTypesVisible.get()) {\n            return a.isVisible() || hiddenTypesVisible.get();\n        } else if (type != AccountType.INCOME && type != AccountType.EXPENSE && accountTypesVisible.get()) {\n            return a.isVisible() || hiddenTypesVisible.get();\n        }\n        return false;\n    }\n\n    /**\n     * Adds a single {@code InvalidationListener} to all visibility properties.\n     *\n     * @param listener {@code InvalidationListener} to add\n     */\n    public void addListener(final InvalidationListener listener) {\n        getAccountTypesVisibleProperty().addListener(listener);\n        getExpenseTypesVisibleProperty().addListener(listener);\n        getIncomeTypesVisibleProperty().addListener(listener);\n        getHiddenTypesVisibleProperty().addListener(listener);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/FXMLUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.lang.reflect.Field;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\n\n/**\n * FXML Utility methods.\n *\n * @author Craig Cavanaugh\n */\npublic class FXMLUtils {\n\n    private FXMLUtils() {\n        // Utility class\n    }\n\n    /**\n     * A pair consisting of the Stage and fxml controller.\n     *\n     * @param <C> the fxml controller type\n     */\n    public static class Pair<C> {\n        private final C controller;\n        private final Stage stage;\n\n        Pair(final C controller, final Stage stage) {\n            this.controller = controller;\n            this.stage = stage;\n        }\n\n        public C getController() {\n            return controller;\n        }\n\n        public Stage getStage() {\n            return stage;\n        }\n    }\n\n    /**\n     * Loads a scene and sets the specified {@code Stage} as the root and controller.  Application defaults are\n     * set for the {@code Stage}\n     *\n     * @param stage          {@code Stage}\n     * @param fileName       name of the fxml file.  It's assumed to be in the same package as the stage\n     * @param resourceBundle {@code ResourceBundle} to pass to the {@code FXMLLoader}\n     */\n    public static void loadFXML(@NotNull final Stage stage, @NotNull final String fileName,\n                                @NotNull final ResourceBundle resourceBundle) {\n        final FXMLLoader fxmlLoader = new FXMLLoader(stage.getClass().getResource(fileName), resourceBundle);\n        fxmlLoader.setRoot(stage);\n        fxmlLoader.setController(stage);\n\n        try {\n            fxmlLoader.load();\n        } catch (final IOException e) {\n            Logger.getLogger(stage.getClass().getName()).log(Level.SEVERE, e.getMessage(), e);\n        }\n\n        stage.initOwner(MainView.getPrimaryStage());\n        ThemeManager.applyStyleSheets(stage.getScene());\n        stage.getScene().getRoot().styleProperty().bind(ThemeManager.styleProperty());\n        stage.initStyle(StageStyle.DECORATED);\n        stage.initModality(Modality.APPLICATION_MODAL);\n        stage.getIcons().add(StaticUIMethods.getApplicationIcon());\n    }\n\n    /**\n     * Reduces boilerplate code to load a fxml file.\n     *\n     * @param consumer       {@code Consumer to pass to the parent node},\n     * @param fileName       name of the fxml file.  It's assumed to be in the same package as the consumer\n     * @param resourceBundle {@code ResourceBundle} to pass to the {@code FXMLLoader}\n     * @param <R>            must extend {@code Node}\n     * @param <C>            the fxml controller\n     * @return the controller for the fxml file\n     */\n    public static <R extends Node, C> C loadFXML(@NotNull final Consumer<R> consumer, @NotNull final String fileName,\n                                                 @NotNull final ResourceBundle resourceBundle) {\n        final URL fxmlUrl = consumer.getClass().getResource(fileName);\n        final FXMLLoader fxmlLoader = new FXMLLoader(fxmlUrl, resourceBundle);\n\n        try {\n            R root = fxmlLoader.load();\n            C controller = fxmlLoader.getController();\n            consumer.accept(root);\n\n            root.styleProperty().bind(ThemeManager.styleProperty());\n\n            // Inject the root into the controller\n            injectParent(controller, root);\n\n            return controller;\n        } catch (final IOException ioe) { // log and throw an unchecked exception\n            Logger.getLogger(FXMLUtils.class.getName()).log(Level.SEVERE, ioe.getMessage(), ioe);\n            throw new UncheckedIOException(ioe);\n        }\n    }\n\n    private static Field[] getDeclaredFields(final Class<?> clazz) {\n        final List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));\n\n        if (clazz.getSuperclass() != null) {\n            fields.addAll(Arrays.asList(getDeclaredFields(clazz.getSuperclass())));\n        }\n        return fields.toArray(new Field[0]);\n    }\n\n    /**\n     * Injects a value into a field of initialized type {@code ObjectProperty}.  The field must also be annotated\n     * with {@code javax.inject.Inject}\n     *\n     * @param object {@code Object} to search for field\n     * @param value  value to set\n     * @see javafx.beans.property.ObjectProperty\n     * @see InjectFXML\n     */\n    @SuppressWarnings(\"unchecked\")\n    private static void injectParent(final Object object, final Object value) {\n        for (final Field field : getDeclaredFields(object.getClass())) {\n            if (field.isAnnotationPresent(InjectFXML.class) && field.getName().equals(\"parent\")) {\n                field.setAccessible(true);\n                try {\n                    final ObjectProperty<Object> property = (ObjectProperty<Object>) field.get(object);\n                    property.set(value);\n                } catch (IllegalAccessException e) {\n                    Logger.getLogger(FXMLUtils.class.getName()).log(Level.SEVERE, e.getMessage(), e);\n                }\n            }\n        }\n    }\n\n    /**\n     * Creates a new Stage with application defaults {@code StageStyle.DECORATED}, {@code Modality.APPLICATION_MODAL}\n     * with the specified fxml file as the {@code Scene}.\n     *\n     * @param controller     controller object to pass to the {@code FXMLLoader}\n     * @param fileName       name of the fxml file.  It's assumed to be in the same package as the controller\n     * @param resourceBundle {@code ResourceBundle} to pass to the {@code FXMLLoader}\n     * @return new {@code Stage}\n     */\n    public static Stage loadFXML(final Object controller, final String fileName, final ResourceBundle resourceBundle) {\n        final URL fxmlUrl = controller.getClass().getResource(fileName);\n        final FXMLLoader fxmlLoader = new FXMLLoader(fxmlUrl, resourceBundle);\n\n        final Stage stage = new Stage(StageStyle.DECORATED);\n        stage.initModality(Modality.APPLICATION_MODAL);\n\n        if (MainView.getInstance() != null) {    // null check is only necessary to pass unit tests\n            stage.initOwner(MainView.getPrimaryStage());\n        }\n\n        try {\n            fxmlLoader.setController(controller);\n\n            final Scene scene = new Scene(fxmlLoader.load());\n            ThemeManager.applyStyleSheets(scene);\n            scene.getRoot().styleProperty().bind(ThemeManager.styleProperty());\n\n            stage.setScene(scene);\n            stage.getIcons().add(StaticUIMethods.getApplicationIcon());\n\n            stage.sizeToScene();    // force a resize, some stages need a push\n\n            // Inject the scene into the controller\n            injectParent(controller, scene);\n        } catch (final IOException ioe) { // log and throw an unchecked exception\n            Logger.getLogger(FXMLUtils.class.getName()).log(Level.SEVERE, ioe.getMessage(), ioe);\n            throw new UncheckedIOException(ioe);\n        }\n\n        return stage;\n    }\n\n    /**\n     * Simple FXML loader that handles exceptions.\n     *\n     * @param fxmlUrl        the fxml {@code URL}\n     * @param resourceBundle {@code ResourceBundle} to pass to the {@code FXMLLoader}\n     * @param <T>            expected return type being loaded\n     * @return The loaded object hierarchy.\n     * @see FXMLLoader#load()\n     */\n    public static <T> T load(final URL fxmlUrl, final ResourceBundle resourceBundle) {\n        final FXMLLoader fxmlLoader = new FXMLLoader(fxmlUrl, resourceBundle);\n\n        try {\n            return fxmlLoader.load();\n        } catch (IOException e) {\n            StaticUIMethods.displayException(e);\n        }\n\n        return null;\n    }\n\n    /**\n     * Creates a new Stage with application defaults {@code StageStyle.DECORATED}, {@code Modality.APPLICATION_MODAL}\n     * with the specified fxml {@code URL} as the {@code Scene}.  The default resource bundle is used.\n     *\n     * @param fxmlUrl the fxml {@code URL}\n     * @param title the Stage title\n     * @param <C> the fxml controller type\n     * @return Pair containing the Stage and controller\n     */\n    public static <C> Pair<C> load(@NotNull final URL fxmlUrl, final String title) {\n        final FXMLLoader fxmlLoader = new FXMLLoader(fxmlUrl, ResourceUtils.getBundle());\n\n        final Stage stage = new Stage(StageStyle.DECORATED);\n        stage.initModality(Modality.APPLICATION_MODAL);\n        stage.initOwner(MainView.getPrimaryStage());\n\n        try {\n            final Scene scene = new Scene(fxmlLoader.load());\n            ThemeManager.applyStyleSheets(scene);\n            scene.getRoot().styleProperty().bind(ThemeManager.styleProperty());\n\n            final C controller = fxmlLoader.getController();\n\n            stage.setScene(scene);\n            stage.getIcons().add(StaticUIMethods.getApplicationIcon());\n            stage.setTitle(title);\n\n            stage.sizeToScene();    // force a resize, some stages need a push\n\n            // Inject the scene into the controller\n            injectParent(controller, scene);\n\n            stage.setOnShown(event -> JavaFXUtils.runNow(() -> {\n                stage.sizeToScene();    // force the stage to resize to the scene before setting the minimums\n\n                stage.setMinHeight(stage.getHeight());\n                stage.setMinWidth(stage.getWidth());\n            }));\n\n            return new Pair<>(controller, stage);\n        } catch (final IOException ioe) { // log and throw an unchecked exception\n            Logger.getLogger(FXMLUtils.class.getName()).log(Level.SEVERE, ioe.getMessage(), ioe);\n            throw new UncheckedIOException(ioe);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/FileChooserFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Factory class for FileChoosers\n */\npublic final class FileChooserFactory {\n\n    /**\n     * Returns a {@code FileChooser} configured to filter all known jGnash file types\n     * @return a configured FileChooser\n     */\n    public static FileChooser getDataStoreChooser() {\n        return getDataStoreChooser(DataStoreType.values());\n    }\n\n    /**\n     * Returns a {@code FileChooser} configured to filter the supplied jGnash file types\n     *\n     * @param types {@code DataStoreType} to filter on\n     * @return a configured FileChooser\n     */\n    public static FileChooser getDataStoreChooser(final DataStoreType... types) {\n        final FileChooser fileChooser = new FileChooser();\n\n        final String[] ext = new String[types.length];\n\n        final StringBuilder description = new StringBuilder(ResourceUtils.getString(\"Label.jGnashFiles\") + \" (\");\n\n        for (int i = 0; i < types.length; i++) {\n            ext[i] = \"*\" + types[i].getDataStore().getFileExt();\n\n            description.append(\"*\");\n            description.append(types[i].getDataStore().getFileExt());\n\n            if (i < types.length - 1) {\n                description.append(\", \");\n            }\n        }\n        description.append(')');\n\n        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(description.toString(), ext));\n\n        return fileChooser;\n    }\n\n    private FileChooserFactory() {\n        // Utility class\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/InjectFXML.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\n/**\n * A simple Injection annotation.\n */\n@Target({FIELD})\n@Retention(RUNTIME)\npublic @interface InjectFXML {\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/JavaFXUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport java.time.LocalDateTime;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.FutureTask;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.TimeUnit;\n\nimport javafx.application.Platform;\nimport javafx.collections.ObservableList;\nimport javafx.geometry.Orientation;\nimport javafx.print.PageLayout;\nimport javafx.print.PageOrientation;\nimport javafx.print.Printer;\nimport javafx.print.PrinterJob;\nimport javafx.scene.Group;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.image.ImageView;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.input.KeyCodeCombination;\nimport javafx.scene.input.KeyCombination;\nimport javafx.scene.text.Text;\nimport javafx.scene.transform.Scale;\n\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Utility methods for  managing the Application thread and {@code Scene}.\n *\n * @author Craig Cavanaugh\n */\npublic class JavaFXUtils {\n\n    public static final KeyCombination ENTER_KEY = new KeyCodeCombination(KeyCode.ENTER);\n\n    public static final KeyCombination ESCAPE_KEY = new KeyCodeCombination(KeyCode.ESCAPE);\n\n    private static final Queue<Runnable> platformRunnables = new ConcurrentLinkedQueue<>();\n\n    /**\n     * The maximum batch period is 500 millis before a respawn\n     */\n    private static final int MAX_BATCH_TIME_MILLIS = 500;\n\n    private static final Semaphore batchSemaphore = new Semaphore(1);\n\n    private JavaFXUtils() {\n        // utility class\n    }\n\n    /**\n     * Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future.\n     * <p>\n     * This implementation batches Runnables together in the order received to minimize stress on the JavaFX Application\n     * Thread.  A small delay between batches is enforced if being flooded by too many events.  Do not use this for\n     * time sensitive operation such a UI feedback where delays are not desirable\n     *\n     * @param runnable the Runnable whose run method will be executed on the JavaFX Application Thread\n     *\n     * @see Platform#runLater(Runnable)\n     */\n    public static void runLater(@NotNull final Runnable runnable) {\n        platformRunnables.add(runnable);\n\n        // do not call _runLater unless the semaphore has been released.  _runLater will respawn if needed\n        if (batchSemaphore.tryAcquire()) {\n            _runLater();\n        }\n    }\n\n    /**\n     * Run the specified Runnable on the JavaFX Application Thread prior to other queued runnables and without yielding.\n     *\n     * @param runnable runnable the Runnable whose run method will be executed on the JavaFX Application Thread\n     */\n    public static void runNow(@NotNull final Runnable runnable) {\n       Platform.runLater(runnable);    // bypass the queue\n    }\n\n    private static synchronized void _runLater() {\n\n        // don't flood the JavaFX Application with too many Runnables at once.\n        // Allow other internal JavaFX processes to run by yielding\n        if (!platformRunnables.isEmpty()) {\n            Thread.yield();\n        }\n\n        Platform.runLater(() -> {\n            final LocalDateTime start = LocalDateTime.now();\n\n            //int count = 0;\n\n            while (ChronoUnit.MILLIS.between(start, LocalDateTime.now()) < MAX_BATCH_TIME_MILLIS) {\n                if (!platformRunnables.isEmpty()) {\n                    final Runnable runnable = platformRunnables.poll(); // poll instead of remove to prevent a NPE\n                    if (runnable != null) {\n                        runnable.run();\n                        //count++;\n                    }\n                } else {\n                    break;\n                }\n            }\n\n            //System.out.println(\"batch \" + count + \": \"+ ChronoUnit.MILLIS.between(start, LocalDateTime.now()));\n\n            // if there are still more runnables to process, respawn to empty the queue\n            if (!platformRunnables.isEmpty()) {\n                _runLater();\n            } else {\n                batchSemaphore.release();   // release the semaphore now the queue is empty\n            }\n        });\n    }\n\n    /**\n     * Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future and\n     * wait for completion.\n     *\n     * @param runnable the Runnable whose run method will be executed on the JavaFX Application Thread\n     */\n    @SuppressWarnings(\"WeakerAccess\")\n    public static void runAndWait(@NotNull final Runnable runnable) {\n\n        if (Platform.isFxApplicationThread()) { // run synchronously if already on the Application Thread\n            try {\n                runnable.run();\n            } catch (final Exception e) {\n                logSevere(JavaFXUtils.class, e);\n            }\n        } else {\n            final CountDownLatch doneLatch = new CountDownLatch(1);\n            runLater(() -> {\n                try {\n                    runnable.run();\n                } catch (final Exception e) {\n                    logSevere(JavaFXUtils.class, e);\n                } finally {\n                    doneLatch.countDown();\n                }\n            });\n\n            try {\n                doneLatch.await(1, TimeUnit.MINUTES);   // protect against an infinite hang\n            } catch (final InterruptedException e) {\n                logSevere(JavaFXUtils.class, e);\n                Thread.currentThread().interrupt();\n            }\n        }\n    }\n\n    /**\n     * Focuses the next node within a {@code Parent}.\n     *\n     * @param node {@code Node} predecessor node\n     */\n    public static void focusNext(final Node node) {\n\n        final Parent parent = node.getParent();\n\n        if (parent != null) {\n            final ObservableList<Node> children = parent.getChildrenUnmodifiable();\n            final int index = children.indexOf(node);\n\n            if (index >= 0) {\n                // step through children after this node\n                for (int i = index + 1; i < children.size(); i++) {\n                    if (children.get(i).isFocusTraversable()) {\n                        children.get(i).requestFocus();\n                        break;\n                    }\n                }\n\n                // wrap to the start\n                for (int i = 0; i < index; i++) {\n                    if (children.get(i).isFocusTraversable()) {\n                        children.get(i).requestFocus();\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\n    public static Optional<ScrollBar> findVerticalScrollBar(final Node table) {\n        for (final Node node : table.lookupAll(\".scroll-bar\")) {\n            if (node instanceof ScrollBar) {\n                if (((ScrollBar) node).getOrientation() == Orientation.VERTICAL) {\n                    return Optional.of((ScrollBar) node);\n                }\n            }\n        }\n\n        return Optional.empty();\n    }\n\n    /*public static ScrollBar findHorizontalScrollBar(final Node table) {\n        for (final Node node : table.lookupAll(\".scroll-bar:horizontal\")) {\n            if (node instanceof ScrollBar) {\n                if (((ScrollBar) node).getOrientation() == Orientation.HORIZONTAL) {\n                    return (ScrollBar) node;\n                }\n            }\n        }\n\n        throw new RuntimeException(\"Could not find horizontal scroll bar\");\n    }*/\n\n    /**\n     * Calculates the displayed width of a text string.\n     *\n     * @param displayString displayed text\n     * @param style         text style, may be null\n     * @return width of the displayed string\n     */\n    public static double getDisplayedTextWidth(@NotNull final String displayString, @Nullable final String style) {\n        double width = 0;\n\n        // Text and Scene construction must be done on the Platform thread\n        // Invoke the task on the platform thread and wait until complete\n\n        if (!displayString.isEmpty()) {    // ignore empty strings\n            if (Platform.isFxApplicationThread()) {\n                width = _getDisplayedTextWidth(displayString, style);\n            } else {\n                // Text and Scene construction must be done on the Platform thread\n                // Invoke the task on the platform thread and wait until complete\n                final FutureTask<Double> futureTask =\n                        new FutureTask<>(() -> _getDisplayedTextWidth(displayString, style));\n                JavaFXUtils.runNow(futureTask);\n\n                try {\n                    width = futureTask.get();\n                } catch (final InterruptedException | ExecutionException e) {\n                    logSevere(JavaFXUtils.class, e);\n                }\n            }\n        }\n\n        return width;\n    }\n\n\tprivate static double _getDisplayedTextWidth(@NotNull final String displayString, @Nullable final String style) {\n        final Text text = new Text(displayString);\n        \n        new Scene(new Group(text));\t// text must be placed into a Scene for bounds to be calculated correctly.\n\n        text.setStyle(style);\n        text.applyCss();\n\n        return Math.ceil(text.getLayoutBounds().getWidth()) * ThemeManager.fontScaleProperty().doubleValue();\n    }\n\n    /**\n     * Sends an {@code ImageView} to a printer.\n     *\n     * @param imageView {@code ImageView} to print\n     */\n    public static void printImageView(final ImageView imageView) {\n        final PrinterJob job = PrinterJob.createPrinterJob();\n\n        if (job != null) {\n            // Get the default page layout\n            final Printer printer = Printer.getDefaultPrinter();\n            PageLayout pageLayout = job.getJobSettings().getPageLayout();\n\n            // Request landscape orientation by default\n            pageLayout = printer.createPageLayout(pageLayout.getPaper(), PageOrientation.LANDSCAPE,\n                    Printer.MarginType.DEFAULT);\n\n            job.getJobSettings().setPageLayout(pageLayout);\n\n            if (job.showPageSetupDialog(MainView.getPrimaryStage())) {\n                pageLayout = job.getJobSettings().getPageLayout();\n\n                // determine the scaling factor to fit the page\n                final double scale = Math.min(pageLayout.getPrintableWidth() / imageView.getBoundsInParent().getWidth(),\n                        pageLayout.getPrintableHeight() / imageView.getBoundsInParent().getHeight());\n\n                imageView.getTransforms().add(new Scale(scale, scale));\n\n                if (job.printPage(imageView)) {\n                    job.endJob();\n                }\n            } else {\n                job.cancelJob();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/StageUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.geometry.Rectangle2D;\nimport javafx.stage.Screen;\nimport javafx.stage.Stage;\nimport javafx.stage.Window;\n\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.Nullable;\n\nimport org.apache.commons.math3.util.Precision;\n\n/**\n * Saves and restores Stage sizes.\n *\n * @author Craig Cavanaugh\n */\npublic class StageUtils {\n\n    private static final String DEFAULT_KEY = \"bounds\";\n\n    private static final char COMMA_DELIMITER = ',';\n\n    private static final int X = 0;\n\n    private static final int Y = 1;\n\n    private static final int WIDTH = 2;\n\n    private static final int HEIGHT = 3;\n\n    private static final int UPDATE_PERIOD = 2; // update period in seconds\n\n    /**\n     * Restores and saves the size and location of a stage.\n     *\n     * @param stage    The stage to save and restore size and position\n     * @param prefNode This should typically be the calling controller\n     */\n    public static void addBoundsListener(final Stage stage, final Class<?> prefNode) {\n        addBoundsListener(stage, prefNode.getName().replace('.', '/'), null);\n    }\n\n    /**\n     * Restores and saves the size and location of a stage.\n     *\n     * @param stage    The stage to save and restore size and position\n     * @param prefNode This should typically be the calling controller\n     */\n    public static void addBoundsListener(final Stage stage, final Class<?> prefNode, @Nullable final Stage parent) {\n        addBoundsListener(stage, prefNode.getName().replace('.', '/'), parent);\n    }\n\n    public static void addBoundsListener(final Stage stage, final String prefNode, @Nullable final Stage parent) {\n        final String bounds = Preferences.userRoot().node(prefNode).get(DEFAULT_KEY, null);\n\n        if (bounds != null) { // restore to previous size and position\n            Rectangle2D rectangle = decodeRectangle(bounds);\n\n            // relative window placement requested.  Modify the coordinates to the current parent placement\n            if (parent != null) {\n                rectangle = new Rectangle2D(rectangle.getMinX() + parent.getX(),\n                        rectangle.getMinY() + parent.getY(), rectangle.getWidth(), rectangle.getHeight());\n            }\n\n            // Do not try to restore bounds if they exceed available screen space.. user dropped a monitor\n            if (getMaxVisualBounds().contains(rectangle)) {\n                final boolean resizable = stage.isResizable();\n\n                // Stage will not reposition if resizable is false... JavaFx bug?\n                stage.setResizable(false);\n\n                stage.setX(rectangle.getMinX());\n                stage.setY(rectangle.getMinY());\n\n                if (resizable) { // don't resize if originally false\n                    if (!Precision.equals(stage.getMinWidth(), stage.getMaxWidth())) {   // width may be locked\n                        final double width = rectangle.getWidth();\n\n                        if (stage.isShowing()) {\n                            JavaFXUtils.runNow(() -> stage.setWidth(width));\n                        } else {\n                            stage.setWidth(width);\n                        }\n\n                    }\n\n                    if (!Precision.equals(stage.getMinHeight(), stage.getMaxHeight())) { // height may be locked\n                        final double height = rectangle.getHeight();\n\n                        if (stage.isShowing()) {\n                            JavaFXUtils.runNow(() -> stage.setHeight(height));\n                        } else {\n                            stage.setHeight(height);\n                        }\n                    }\n                }\n                stage.setResizable(resizable); // restore the resize property\n            }\n        }\n\n        final ChangeListener<Number> boundsListener = new BoundsListener(stage, prefNode, parent);\n\n        stage.widthProperty().addListener(boundsListener);\n        stage.heightProperty().addListener(boundsListener);\n        stage.xProperty().addListener(boundsListener);\n        stage.yProperty().addListener(boundsListener);\n    }\n\n    /**\n     * Save Window bounds.  Limits rate of saves to the preferences system\n     */\n    private static class BoundsListener implements ChangeListener<Number> {\n        private final ScheduledThreadPoolExecutor executor;\n        private final Preferences p;\n        private final Window window;\n        private final Stage parent;\n\n        BoundsListener(final Window window, final String prefNode, @Nullable final Stage parent) {\n            executor = new ScheduledThreadPoolExecutor(1,\n                    new DefaultDaemonThreadFactory(\"Stage Bounds Listener Executor\"),\n                    new ThreadPoolExecutor.DiscardPolicy());\n\n            p = Preferences.userRoot().node(prefNode);\n            this.window = window;\n            this.parent = parent;\n        }\n\n        @Override\n        public void changed(final ObservableValue<? extends Number> observable, final Number old, final Number newNum) {\n            executor.schedule(() -> {\n                if (executor.getQueue().size() < 1) {   // ignore if we already have one waiting in the queue\n                    // window size and location requests must be pushed to the EDT to prevent a race condition\n                    JavaFXUtils.runLater(() -> {\n                        if (parent != null) {\n                            p.put(DEFAULT_KEY, encodeRectangle(window.getX() - parent.getX(),\n                                    window.getY() - parent.getY(), window.getWidth(), window.getHeight()));\n                        } else {\n                            p.put(DEFAULT_KEY, encodeRectangle(window.getX(), window.getY(), window.getWidth(),\n                                    window.getHeight()));\n                        }\n                    });\n                }\n            }, UPDATE_PERIOD, TimeUnit.SECONDS);\n        }\n    }\n\n    private static String encodeRectangle(final double x, final double y, final double width, final double height) {\n        return Double.toString(x) + COMMA_DELIMITER + y + COMMA_DELIMITER + width + COMMA_DELIMITER + height;\n    }\n\n    private static Rectangle2D decodeRectangle(final String bounds) {\n        if (bounds == null) {\n            return null;\n        }\n\n        Rectangle2D rectangle = null;\n\n        final String[] array = bounds.split(String.valueOf(COMMA_DELIMITER));\n\n        if (array.length == 4) {\n            try {\n                rectangle = new Rectangle2D(Double.parseDouble(array[X]), Double.parseDouble(array[Y]),\n                        Double.parseDouble(array[WIDTH]), Double.parseDouble(array[HEIGHT]));\n            } catch (final NumberFormatException nfe) {\n                Logger.getLogger(StageUtils.class.getName()).log(Level.SEVERE, null, nfe);\n            }\n        }\n\n        return rectangle;\n    }\n\n    /**\n     * Returns the maximum visual bounds of the users desktop.\n     *\n     * @return maximum usable desktop bounds\n     */\n    private static Rectangle2D getMaxVisualBounds() {\n        double maxX = 0;\n        double maxY = 0;\n        double minX = Double.MAX_VALUE;\n        double minY = Double.MAX_VALUE;\n\n        for (final Screen screen : Screen.getScreens()) {\n            minX = Math.min(minX, screen.getVisualBounds().getMinX());\n            minY = Math.min(minY, screen.getVisualBounds().getMinY());\n\n            maxX = Math.max(maxX, screen.getVisualBounds().getMaxX());\n            maxY = Math.max(maxY, screen.getVisualBounds().getMaxY());\n        }\n\n        return new Rectangle2D(minX, minY, maxX - minX, maxY - minY);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/TableViewManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport java.text.Format;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.OptionalDouble;\nimport java.util.Set;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.function.Supplier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableColumnBase;\nimport javafx.scene.control.TableView;\nimport javafx.util.Callback;\n\nimport jgnash.util.EncodeDecode;\nimport jgnash.util.NotNull;\n\n/**\n * TableView manager.  Handles persistence of column sizes, visibility and will optimize column widths\n *\n * @author Craig Cavanaugh\n */\npublic class TableViewManager<S> {\n\n    //private static final String PREF_NODE_REG_POS = \"/positions\";\n\n    private static final String LAST_RECALCULATION = \"/recalculation\";\n\n    private static final String PREF_NODE_REG_VIS = \"/visibility\";\n\n    private static final String PREF_NODE_REG_WIDTH = \"/width\";\n\n    private static final int COLUMN_PADDING = 10; // margins need extra padding to prevent truncated display\n\n    // TODO: Extract or calculate when JavaFX font metrics API improves\n    private static final double BOLD_MULTIPLIER = 1.08;  // multiplier for bold width\n    private static final double COLUMN_BORDER_WIDTH = 2.0;\n\n    /**\n     * Ensure visible columns do not disappear\n     */\n    private static final int MIN_WIDTH = 30;\n\n    /**\n     * JavaFX default value for maximum column width\n     */\n    private static final int MAX_WIDTH = 4000;\n\n    @NotNull\n    private final TableView<S> tableView;\n\n    private final String preferencesUserRoot;\n\n    private final ObjectProperty<Callback<TableColumnBase<S, ?>, Format>> columnFormatFactory = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Callback<Integer, Boolean>> columnVisibilityFactory = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Callback<Integer, Double>> columnWeightFactory = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Callback<Integer, Double>> minimumColumnWidthFactory = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Supplier<String>> preferenceKeyFactory = new SimpleObjectProperty<>();\n\n    private final ColumnVisibilityListener visibilityListener = new ColumnVisibilityListener();\n\n    private final ColumnWidthListener columnWidthListener = new ColumnWidthListener();\n\n    /**\n     * If true, manual packing for the table should be allowed\n     */\n    private final BooleanProperty manualPacking = new SimpleBooleanProperty(false);\n\n    /**\n     * Limits number of processed visibility change events ensuring the most recent is executed.\n     */\n    private final ThreadPoolExecutor updateColumnVisibilityExecutor;\n\n    /**\n     * Limits number of packTable calls while ensuring the most recent is executed.\n     */\n    private final ThreadPoolExecutor packTableExecutor;\n\n    /**\n     * Limits number of saveColumnWidths calls while ensuring the most recent is executed.\n     */\n    private final ThreadPoolExecutor saveColumnWidthExecutor;\n\n    /**\n     * Used to track initialization and pack state\n     */\n    private final AtomicLong packCounter = new AtomicLong();\n\n    public TableViewManager(@NotNull final TableView<S> tableView, @NotNull final String preferencesUserRoot) {\n        this.tableView = tableView;\n        this.preferencesUserRoot = preferencesUserRoot;\n\n        // Set a default factories\n        setColumnFormatFactory(param -> null);\n        setMinimumColumnWidthFactory(param -> 0.0);\n\n        /* At least 2 updates need to be allowed.  The update in process and any potential updates requested\n         * that occur when an update is already in process.  Limited to 1 thread\n         *\n         * Excess execution requests will be silently discarded\n         */\n        updateColumnVisibilityExecutor = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE,\n                TimeUnit.DAYS, new ArrayBlockingQueue<>(1));\n        updateColumnVisibilityExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());\n\n        packTableExecutor = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE,\n                TimeUnit.DAYS, new ArrayBlockingQueue<>(1));\n        packTableExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());\n\n        saveColumnWidthExecutor = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE,\n                TimeUnit.DAYS, new ArrayBlockingQueue<>(1));\n        saveColumnWidthExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());\n\n        // repack and reset column properties if the user changes settings\n        manualPacking.addListener((observable, oldValue, newValue) -> {\n            if (packCounter.get() > 0) {\n                packTable();\n            }\n        });\n    }\n\n    public void restoreLayout() {\n        if (packCounter.get() == 0) {\n            JavaFXUtils.runAndWait(this::restoreColumnVisibility); // Restore visibility first\n            JavaFXUtils.runLater(this::packTable);  // pack the table\n        }\n    }\n\n    private void installColumnListeners() {\n        for (final TableColumnBase<S, ?> tableColumn : tableView.getColumns()) {\n            tableColumn.visibleProperty().addListener(visibilityListener);\n            tableColumn.widthProperty().addListener(columnWidthListener);\n        }\n    }\n\n    private void removeColumnListeners() {\n        for (final TableColumnBase<S, ?> tableColumn : tableView.getColumns()) {\n            tableColumn.visibleProperty().removeListener(visibilityListener);\n            tableColumn.widthProperty().removeListener(columnWidthListener);\n        }\n    }\n\n    public BooleanProperty manualColumnPackingProperty() {\n        return manualPacking;\n    }\n\n    /**\n     * Determines the preferred width of the column including contents.\n     *\n     * @param column {@code TableColumn} to measure content\n     * @return preferred width\n     */\n    private double getCalculatedColumnWidth(final TableColumnBase<S, ?> column) {\n        double maxWidth = 0;\n\n        /* Collect all the unique cell items and remove null*/\n        final Set<Object> cellItems = new HashSet<>();\n\n        for (int i = 0; i < tableView.getItems().size(); i++) {\n            cellItems.add(column.getCellData(i));\n        }\n        cellItems.remove(null);\n\n        if (cellItems.size() > 0) { // don't try if there is no data or the stream function will throw an error\n\n            final OptionalDouble max = cellItems.parallelStream().filter(Objects::nonNull).mapToDouble(o -> {\n                final Format format = columnFormatFactory.get().call(column);   // thread local format per thread\n                return JavaFXUtils.getDisplayedTextWidth(format != null ? format.format(o) : o.toString(), column.getStyle());\n            }).max();\n\n            maxWidth = max.isPresent() ? max.getAsDouble() : 0;\n        }\n\n        //noinspection SuspiciousMethodCalls\n        maxWidth = Math.max(maxWidth, Math.max(column.getMinWidth(), minimumColumnWidthFactory\n                .get().call(tableView.getColumns().indexOf(column))));\n\n        // header text width\n        maxWidth = Math.max(maxWidth,\n                JavaFXUtils.getDisplayedTextWidth(column.getText(), column.getStyle()) * BOLD_MULTIPLIER);\n\n        return Math.ceil(maxWidth + COLUMN_PADDING);\n    }\n\n    private void saveColumnWidths() {\n        JavaFXUtils.runLater(() -> {\n            if (preferenceKeyFactory.get() != null) {\n                final double[] columnWidths = tableView.getColumns().filtered(TableColumnBase::isVisible).stream()\n                        .mapToDouble(value -> Math.floor(value.getWidth())).toArray();\n\n                final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + PREF_NODE_REG_WIDTH);\n\n                preferences.put(preferenceKeyFactory.get().get(), EncodeDecode.encodeDoubleArray(columnWidths));\n            }\n        });\n    }\n\n    private void saveColumnVisibility() {\n        if (preferenceKeyFactory.get() != null) {\n            final String uuid = preferenceKeyFactory.get().get();\n            final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + PREF_NODE_REG_VIS);\n\n            final boolean[] columnVisibility = new boolean[tableView.getColumns().size()];\n\n            for (int i = 0; i < columnVisibility.length; i++) {\n                columnVisibility[i] = tableView.getColumns().get(i).isVisible();\n            }\n\n            preferences.put(uuid, EncodeDecode.encodeBooleanArray(columnVisibility));\n        }\n    }\n\n    private void clearTimeStamp() {\n        if (preferenceKeyFactory.get() != null) {\n            final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + LAST_RECALCULATION);\n            preferences.putLong(preferenceKeyFactory.get().get(), 0);\n        }\n    }\n\n    private void saveTimeStamp() {\n        if (preferenceKeyFactory.get() != null) {\n            final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + LAST_RECALCULATION);\n            preferences.putLong(preferenceKeyFactory.get().get(), System.currentTimeMillis());\n        }\n    }\n\n    public long getTimeStamp() {\n        if (preferenceKeyFactory.get() != null) {\n            final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + LAST_RECALCULATION);\n            return preferences.getLong(preferenceKeyFactory.get().get(), 0);\n        }\n        return 0;\n    }\n\n    private void restoreColumnVisibility() {\n        if (preferenceKeyFactory.get() != null) {\n\n            // Remove listeners while state is being restored so states are not saved during state changes\n            removeColumnListeners();\n\n            final String uuid = preferenceKeyFactory.get().get();\n            final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + PREF_NODE_REG_VIS);\n\n            final String result = preferences.get(uuid, null);\n            if (result != null) {\n                final boolean[] columnVisibility = EncodeDecode.decodeBooleanArray(result);\n\n                tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);\n\n                if (columnVisibility.length == tableView.getColumns().size()) {\n                    for (int i = 0; i < columnVisibility.length; i++) {\n                        tableView.getColumns().get(i).visibleProperty().set(columnVisibility[i]);\n                    }\n                }\n\n                tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n            } else {  // no preference has been set, so force all columns to be the default\n                final Callback<Integer, Boolean> visibilityCallBack = columnVisibilityFactory.get();\n\n                if (visibilityCallBack != null) {   // use a factory method\n                    int i = 0;\n\n                    for (final TableColumnBase<S, ?> column : tableView.getColumns()) {\n                        column.visibleProperty().set(visibilityCallBack.call(i++));\n                    }\n                } else {\n                    for (final TableColumnBase<S, ?> column : tableView.getColumns()) {\n                        column.visibleProperty().set(true);\n                    }\n                }\n            }\n\n            // restore listeners\n            installColumnListeners();\n        }\n    }\n\n    @NotNull\n    private double[] retrieveOldColumnWidths() {\n        double[] columnWidths = new double[0];  // zero length array instead of null to protect against NPE\n\n        // no need to retrieve old column widths more than once\n        if (preferenceKeyFactory.get() != null) {\n\n            final String uuid = preferenceKeyFactory.get().get();\n            final Preferences preferences = Preferences.userRoot().node(preferencesUserRoot + PREF_NODE_REG_WIDTH);\n\n            final String widths = preferences.get(uuid, null);\n\n            if (widths != null) {\n                columnWidths = EncodeDecode.decodeDoubleArray(widths);\n            }\n        }\n\n        return columnWidths;\n    }\n\n    /**\n     * Called when the table columns need to be repacked because of content change\n     */\n    public synchronized void packTable() {\n        if (tableView.widthProperty().get() == 0) {\n            return; // exit right away if the table view is not visible\n        }\n\n        packCounter.incrementAndGet();\n\n        packTableExecutor.execute(() -> {   // rate limits with a high rate of transactional changes\n\n            // Create a list of visible columns and column weights\n            final List<TableColumn<S, ?>> visibleColumns = new ArrayList<>();\n            final List<Double> visibleColumnWeights = new ArrayList<>();\n\n            for (int i = 0; i < tableView.getColumns().size(); i++) {\n                if (tableView.getColumns().get(i).isVisible()) {\n                    visibleColumns.add(tableView.getColumns().get(i));\n                    visibleColumnWeights.add(columnWeightFactory.get().call(i));\n                }\n                tableView.getColumns().get(i).setMinWidth(0);   // clear minWidth for pack\n            }\n\n            final double[] oldWidths = retrieveOldColumnWidths();\n\n            if (oldWidths.length == visibleColumns.size()) {\n                double sumOldResizableColumns = 0;\n\n                // calculate the sum of visible resizable columns\n                for (int i = 0; i < visibleColumns.size(); i++) {\n                    if (visibleColumnWeights.get(i) > 0) {\n                        sumOldResizableColumns += oldWidths[i];\n                    }\n                }\n\n                for (int i = 0; i < visibleColumns.size(); i++) {\n                    if (visibleColumnWeights.get(i) > 0) {\n                        double oldPercentage = oldWidths[i] / sumOldResizableColumns * 100.0;\n                        visibleColumnWeights.set(i, oldPercentage);\n                    }\n                }\n            }\n\n            /* Use the old calculated widths if the count is correct and full initialization has not occurred */\n            final double[] calculatedWidths = oldWidths.length == visibleColumns.size()\n                    ? oldWidths : new double[visibleColumns.size()];\n\n            /* If any columns are zero width, a full calculation is required */\n            boolean forceCalculations = false;\n\n            for (final double width : calculatedWidths) {\n                if (width <= 0.0) {\n                    forceCalculations = true;\n                    break;\n                }\n            }\n\n            if (forceCalculations || packCounter.get() > 0) {\n                double sumFixedColumns = 0; // sum of the fixed width columns\n\n                /* determine if the expensive calculations needs to occur */\n                final boolean doExpensiveCalculations = oldWidths.length != visibleColumns.size()\n                        || packCounter.get() > 1 || forceCalculations;\n\n                for (int i = 0; i < calculatedWidths.length; i++) {\n                    if (visibleColumnWeights.get(i) == 0) {\n\n                        /* expensive operation, don't calculate if we are reusing older values */\n                        if (doExpensiveCalculations) {\n                            clearTimeStamp();\n                            calculatedWidths[i] = getCalculatedColumnWidth(visibleColumns.get(i));\n                            saveTimeStamp();    // indicate recalculation is complete\n                        }\n                        sumFixedColumns += calculatedWidths[i];\n                    }\n                }\n\n                // leftover visible width of the table.  Column borders need to be considered\n                double remainder = tableView.widthProperty().get()\n                        - ((visibleColumns.size() - 1) * COLUMN_BORDER_WIDTH) - sumFixedColumns;\n\n                // calculate widths for adjustable columns using the remaining visible width\n                for (int i = 0; i < calculatedWidths.length; i++) {\n                    if (visibleColumnWeights.get(i) != 0) {\n                        calculatedWidths[i] = Math.floor(remainder * (visibleColumnWeights.get(i) / 100.0) - 0.5);\n                    }\n                }\n            }\n\n            JavaFXUtils.runLater(() -> {\n                removeColumnListeners();\n\n                // unconstrained is required for resize columns correctly\n                tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);\n\n                // Force the column widths and let the layout policy do the heavy lifting\n                for (int j = 0; j < calculatedWidths.length; j++) {\n                    visibleColumns.get(j).setResizable(true);   // allow resizing\n\n                    if (!manualPacking.get()) {\n                        if (visibleColumnWeights.get(j) == 0) { // fixed width column\n                            visibleColumns.get(j).minWidthProperty().set(calculatedWidths[j]);\n                            visibleColumns.get(j).maxWidthProperty().set(calculatedWidths[j]);\n                            visibleColumns.get(j).setResizable(false);\n                        } else {    // set the preferred size and ensure the column does not disappear\n                            visibleColumns.get(j).minWidthProperty().set(MIN_WIDTH);\n                            visibleColumns.get(j).prefWidthProperty().set(calculatedWidths[j]);\n                        }\n                    } else {\n                        visibleColumns.get(j).minWidthProperty().set(MIN_WIDTH);\n                        visibleColumns.get(j).maxWidthProperty().set(MAX_WIDTH);\n                    }\n                }\n\n                tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);   // restore the old policy\n\n                // Go back and correct the width of adjustable columns at the end of application thread\n                JavaFXUtils.runLater(() -> {\n                    // Go back and correct the width of adjustable columns\n                    for (int j = 0; j < calculatedWidths.length; j++) {\n\n                        // resize if not a fixed width column or manual packing is enabled\n                        if (visibleColumnWeights.get(j) != 0 || manualPacking.get()) {\n                            tableView.resizeColumn(visibleColumns.get(j), calculatedWidths[j]\n                                    - visibleColumns.get(j).getWidth());\n                        }\n                    }\n                });\n\n                /* save the column widths for next time after fully initialized */\n                if (packCounter.get() > 0) {\n                    saveColumnWidths();\n                }\n\n                installColumnListeners();\n            });\n        });\n    }\n\n    /**\n     * Sets a {@code Format} factory.  A Format will be returned for each column.  A null value may\n     * be returned as well.\n     * <p>\n     * The returned Format must be thread safe.\n     *\n     * @param cellFormat Callback to generate a {@code Format} given a {@code TableColumnBase}\n     */\n    public void setColumnFormatFactory(final Callback<TableColumnBase<S, ?>, Format> cellFormat) {\n        this.columnFormatFactory.set(cellFormat);\n    }\n\n    public void setColumnWeightFactory(final Callback<Integer, Double> weightFactory) {\n        this.columnWeightFactory.set(weightFactory);\n    }\n\n    /**\n     * Enforces a minimum width width for a column\n     *\n     * @param widthFactory {@code Callback} returning a non-zero width of a given column\n     */\n    public void setMinimumColumnWidthFactory(final Callback<Integer, Double> widthFactory) {\n        this.minimumColumnWidthFactory.setValue(widthFactory);\n    }\n\n    public void setDefaultColumnVisibilityFactory(final Callback<Integer, Boolean> visibilityFactoryCallback) {\n        columnVisibilityFactory.set(visibilityFactoryCallback);\n    }\n\n    public void setPreferenceKeyFactory(final Supplier<String> keyFactory) {\n        this.preferenceKeyFactory.set(keyFactory);\n    }\n\n    private final class ColumnVisibilityListener implements ChangeListener<Boolean> {\n        @Override\n        public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue,\n                            final Boolean newValue) {\n            updateColumnVisibilityExecutor.execute(TableViewManager.this::saveColumnVisibility);\n            packTable();\n        }\n    }\n\n    private final class ColumnWidthListener implements ChangeListener<Number> {\n\n        private static final int RATE_LIMIT_MILLIS = 175;\n\n        @Override\n        public void changed(final ObservableValue<? extends Number> observable, final Number oldValue,\n                            final Number newValue) {\n            saveColumnWidthExecutor.execute(() -> {\n                try {\n                    Thread.sleep(RATE_LIMIT_MILLIS);  // rate limit the number of saves during drags\n                    saveColumnWidths();\n                } catch (final InterruptedException e) {\n                    Logger.getLogger(TableViewManager.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/util/TreeSearch.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.util;\n\nimport javafx.scene.control.TreeItem;\n\n/**\n * Utility class to search through a JavaFX tree.\n *\n * @author Craig Cavanaugh\n */\npublic class TreeSearch {\n\n    private TreeSearch() {\n        // Utility class\n    }\n\n    public static <T> TreeItem<T> findTreeItem(final TreeItem<T> treeItem, final T value) {\n\n        if (treeItem == null) {\n            return null;\n        }\n\n        if (treeItem.getValue().equals(value)) {\n            return treeItem;\n        }\n        \n\t\tTreeItem<T> childItem;\n\t\t\n\t\tfor (TreeItem<T> child : treeItem.getChildren()) {\n\t\t    if ((childItem = findTreeItem(child, value)) != null) {\n\t\t        return childItem;\n\t\t    }\n\t\t}\n\t\t\n        return null;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/AccountBalanceDisplayManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\n\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\n\nimport java.math.BigDecimal;\nimport java.util.prefs.Preferences;\n\n/**\n * AccountBalanceDisplayManager converts the account balances according to the selected displaying mode.\n * <p>\n * If accounting balances mode is active, then there will not be done any conversion.\n * <p>\n * If the normally positive balances mode is active, then the balances of account groups income, equity and liability\n * will be negated.\n *\n * @author Peter Vida\n * @author Craig Cavanaugh\n */\npublic class AccountBalanceDisplayManager {\n\n    private static final String ACCOUNT_BALANCE_DISPLAY_MODE = \"accountBalanceDisplayMode\";\n\n    private static final ObjectProperty<AccountBalanceDisplayMode> accountBalanceDisplayMode\n            = new SimpleObjectProperty<>();\n\n    static {\n        final Preferences p = Preferences.userNodeForPackage(AccountBalanceDisplayManager.class);\n        accountBalanceDisplayMode().set(AccountBalanceDisplayMode.valueOf(p.get(ACCOUNT_BALANCE_DISPLAY_MODE,\n                AccountBalanceDisplayMode.NONE.name())));\n    }\n\n    private AccountBalanceDisplayManager() {\n        // utility class\n    }\n\n    private static BigDecimal reverseCredit(final AccountType accountType, final BigDecimal balance) {\n        if (accountType.getAccountGroup() == AccountGroup.EQUITY\n                || accountType.getAccountGroup() == AccountGroup.INCOME\n                || accountType.getAccountGroup() == AccountGroup.LIABILITY) {\n            return balance.negate();\n        }\n        return balance;\n    }\n\n    private static BigDecimal reverseIncomeAndExpense(final AccountType accountType, final BigDecimal balance) {\n        if (accountType.getAccountGroup() == AccountGroup.INCOME\n                || accountType.getAccountGroup() == AccountGroup.EXPENSE) {\n            return balance.negate();\n        }\n        return balance;\n    }\n\n    public static BigDecimal convertToSelectedBalanceMode(final AccountType accountType, final BigDecimal balance) {\n        switch (accountBalanceDisplayMode.get()) {\n            case REVERSE_INCOME_EXPENSE:\n                return reverseIncomeAndExpense(accountType, balance);\n            case REVERSE_CREDIT:\n                return reverseCredit(accountType, balance);\n            case NONE:\n            default:\n                return balance;\n        }\n    }\n\n    public static void setDisplayMode(final AccountBalanceDisplayMode newMode) {\n        accountBalanceDisplayMode().setValue(newMode);\n\n        final Preferences p = Preferences.userNodeForPackage(AccountBalanceDisplayManager.class);\n        p.put(ACCOUNT_BALANCE_DISPLAY_MODE, accountBalanceDisplayMode().getValue().name());\n    }\n\n    public static ObjectProperty<AccountBalanceDisplayMode> accountBalanceDisplayMode() {\n        return accountBalanceDisplayMode;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/AccountBalanceDisplayMode.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views;\n\n/**\n * AccountBalanceDisplayingMode defines the modes how the account balances can be displayed.\n *\n * @author Peter Vida\n * @author Craig Cavanaugh\n */\npublic enum AccountBalanceDisplayMode {\n    NONE,\n    REVERSE_CREDIT,\n    REVERSE_INCOME_EXPENSE\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/AccountCommodityFormatTreeTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\n\nimport javafx.scene.control.TreeTableCell;\n\nimport jgnash.engine.Account;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.skin.StyleClass;\n\n/**\n * TreeTable cell for styling positive/negative monetary values.\n *\n * @author Craig Cavanaugh\n */\nclass AccountCommodityFormatTreeTableCell extends TreeTableCell<Account, BigDecimal> {\n\n    AccountCommodityFormatTreeTableCell() {\n        setStyle(\"-fx-alignment: center-right;\");  // Right align\n    }\n\n    @Override\n    protected void updateItem(final BigDecimal amount, final boolean empty) {\n        super.updateItem(amount, empty);  // required\n\n        if (!empty && amount != null && getTreeTableRow().getTreeItem() != null) {\n            final Account account = getTreeTableRow().getTreeItem().getValue();\n\n            final NumberFormat format = NumericFormats.getFullCommodityFormat(account.getCurrencyNode());\n\n            setText(format.format(amount));\n\n            if (amount.signum() < 0) {\n                setId(StyleClass.NORMAL_NEGATIVE_CELL_ID);\n            } else {\n                setId(StyleClass.NORMAL_CELL_ID);\n            }\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/AccountPropertiesController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport java.util.Iterator;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.logging.Logger;\n\nimport javafx.beans.binding.BooleanBinding;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.CurrencyComboBox;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Loads all account properties into a form and returns a template Account based on the form properties.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountPropertiesController {\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    private boolean result = false;\n\n    @FXML\n    private ComboBox<AccountType> accountTypeComboBox;\n\n    @FXML\n    private TextArea notesTextArea;\n\n    @FXML\n    private TextField nameTextField;\n\n    @FXML\n    private TextField descriptionTextField;\n\n    @FXML\n    private IntegerTextField accountCodeField;\n\n    @FXML\n    private TextField accountNumberField;\n\n    @FXML\n    private TextField bankIdField;\n\n    @FXML\n    private CurrencyComboBox currencyComboBox;\n\n    @FXML\n    private CheckBox lockedCheckBox;\n\n    @FXML\n    private CheckBox hideAccountCheckBox;\n\n    @FXML\n    private CheckBox placeholderCheckBox;\n\n    @FXML\n    private CheckBox excludeBudgetCheckBox;\n\n    @FXML\n    private Button parentAccountButton;\n\n    @FXML\n    private Button securitiesButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private Account parentAccount;\n\n    private DisableSecuritiesBinding disableSecuritiesBinding;\n\n    @Nullable private Account baseAccount = null;\n\n    private final Set<SecurityNode> securityNodeSet = new TreeSet<>();\n\n    @FXML\n    public void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        accountTypeComboBox.setCellFactory(param -> new DisabledListCell());    // set cell factory\n        accountTypeComboBox.getItems().addAll(AccountType.values());\n        accountTypeComboBox.setValue(AccountType.BANK); // set default value\n\n        descriptionTextField.setText(resources.getString(\"Word.Description\"));\n        nameTextField.setText(resources.getString(\"Word.Name\"));\n\n        disableSecuritiesBinding = new DisableSecuritiesBinding();\n\n        securitiesButton.disableProperty().bind(disableSecuritiesBinding);\n        accountTypeComboBox.setOnAction(event -> disableSecuritiesBinding.invalidate());\n    }\n\n    void setSelectedCurrency(final CurrencyNode currency) {\n        currencyComboBox.setValue(currency);\n    }\n\n    @FXML\n    private void handleParentAccountAction() {\n        final Optional<Account> optional = StaticAccountsMethods.selectAccount(parentAccount, baseAccount);\n\n        optional.ifPresent(this::setParentAccount);\n    }\n\n    public boolean getResult() {\n        return result;\n    }\n\n    Set<SecurityNode> getSecurityNodes() {\n        return securityNodeSet;\n    }\n\n    void setParentAccount(final Account parentAccount) {\n        this.parentAccount = parentAccount;\n\n        JavaFXUtils.runLater(() -> {\n            if (parentAccount != null) {\n                parentAccountButton.setText(parentAccount.getName());\n\n                if (parentAccount.getAccountType() != AccountType.ROOT) {   // don't force a root account type\n                    accountTypeComboBox.setValue(parentAccount.getAccountType());\n                }\n            }\n        });\n    }\n\n    Account getTemplate() {\n        Account account = new Account(accountTypeComboBox.getValue(), currencyComboBox.getValue());\n\n        account.setAccountCode(accountCodeField.getInteger());\n        account.setAccountNumber(accountNumberField.getText());\n        account.setBankId(bankIdField.getText());\n        account.setName(nameTextField.getText());\n        account.setDescription(descriptionTextField.getText());\n        account.setNotes(notesTextArea.getText());\n        account.setLocked(lockedCheckBox.isSelected());\n        account.setPlaceHolder(placeholderCheckBox.isSelected());\n\n        if (parentAccount != baseAccount) {\n            account.setParent(parentAccount);\n        } else {\n            Logger.getLogger(AccountPropertiesController.class.getName()).warning(\"Prevented an attempt to assign an account's parent to itself\");\n        }\n\n        account.setVisible(!hideAccountCheckBox.isSelected());\n        account.setExcludedFromBudget(excludeBudgetCheckBox.isSelected());\n\n        return account;\n    }\n\n    void loadProperties(@NotNull final Account account) {\n        baseAccount = account;\n        securityNodeSet.clear();\n\n        JavaFXUtils.runLater(() -> {\n            setParentAccount(account.getParent());\n            nameTextField.setText(account.getName());\n            descriptionTextField.setText(account.getDescription());\n            accountCodeField.setInteger(account.getAccountCode());\n            accountNumberField.setText(account.getAccountNumber());\n            bankIdField.setText(account.getBankId());\n            setSelectedCurrency(account.getCurrencyNode());\n            notesTextArea.setText(account.getNotes());\n            lockedCheckBox.setSelected(account.isLocked());\n            hideAccountCheckBox.setSelected(!account.isVisible());\n            excludeBudgetCheckBox.setSelected(account.isExcludedFromBudget());\n\n            if (baseAccount.getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                securityNodeSet.addAll(account.getSecurities());\n                updateCommodityText();\n            }\n\n            accountTypeComboBox.setValue(account.getAccountType());\n\n            if (baseAccount.getTransactionCount() > 0) {\n                placeholderCheckBox.setDisable(true);\n            } else {\n                placeholderCheckBox.setSelected(account.isPlaceHolder());\n            }\n\n            disableSecuritiesBinding.invalidate();\n        });\n    }\n\n    @FXML\n    public void handleSecuritiesButtonAction() {\n\n        final SelectAccountSecuritiesDialog control = new SelectAccountSecuritiesDialog(baseAccount, securityNodeSet);\n\n        if (control.showAndWait()) {\n            securityNodeSet.clear();\n            securityNodeSet.addAll(control.getSelectedSecurities());\n\n            updateCommodityText();\n        }\n    }\n\n    private void updateCommodityText() {\n        if (!securityNodeSet.isEmpty()) {\n            StringBuilder buf = new StringBuilder();\n            Iterator<SecurityNode> it = securityNodeSet.iterator();\n\n            SecurityNode node = it.next();\n            buf.append(node.getSymbol());\n            while (it.hasNext()) {\n                buf.append(\", \");\n                node = it.next();\n                buf.append(node.getSymbol());\n            }\n\n            JavaFXUtils.runLater(() -> {\n                securitiesButton.setText(buf.toString());\n                securitiesButton.setTooltip(new Tooltip(buf.toString()));\n            });\n        } else {\n            JavaFXUtils.runLater(() -> securitiesButton.setText(resources.getString(\"Word.None\")));\n        }\n    }\n    @FXML\n    private void okAction() {\n        result = true;\n        ((Stage) nameTextField.getScene().getWindow()).close();\n    }\n\n    @FXML\n    private void cancelAction() {\n        result = false;\n        ((Stage) nameTextField.getScene().getWindow()).close();\n    }\n\n    private class DisableSecuritiesBinding extends BooleanBinding {\n\n        @Override\n        protected boolean computeValue() {\n            return !(baseAccount != null && baseAccount.memberOf(AccountGroup.INVEST))\n                    && accountTypeComboBox.getValue().getAccountGroup() != AccountGroup.INVEST;\n\n        }\n    }\n\n    /**\n     * Disables selection of the account type if it is not within the same account group as the account being modified.\n     */\n    private class DisabledListCell extends ListCell<AccountType> {\n\n        @Override\n        protected void updateItem(final AccountType item, final boolean empty) {\n            super.updateItem(item, empty);\n\n            if (!empty) {\n                setText(item.toString());\n\n                if (baseAccount != null && baseAccount.getAccountType().getAccountGroup() != item.getAccountGroup()) {\n                    setId(StyleClass.DISABLED_CELL_ID);\n                    setDisable(true);\n                } else {\n                    setId(StyleClass.ENABLED_CELL_ID);\n                    setDisable(false);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/AccountTypeFilterFormController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.stage.Stage;\n\nimport jgnash.uifx.util.AccountTypeFilter;\n\n/**\n * Controller for filter by account type.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountTypeFilterFormController {\n\n    @FXML\n    CheckBox bankAccountCheckBox;\n\n    @FXML\n    CheckBox expenseAccountCheckBox;\n\n    @FXML\n    CheckBox incomeAccountCheckBox;\n\n    @FXML\n    CheckBox hiddenAccountCheckBox;    \n\n    void setAccountTypeFilter(final AccountTypeFilter filter) {\n\n        // Bind the buttons to the filter\n        bankAccountCheckBox.selectedProperty().bindBidirectional(filter.getAccountTypesVisibleProperty());\n        incomeAccountCheckBox.selectedProperty().bindBidirectional(filter.getIncomeTypesVisibleProperty());\n        expenseAccountCheckBox.selectedProperty().bindBidirectional(filter.getExpenseTypesVisibleProperty());\n        hiddenAccountCheckBox.selectedProperty().bindBidirectional(filter.getHiddenTypesVisibleProperty());\n    }\n\n    @FXML\n    private void closeAction() {\n        ((Stage) bankAccountCheckBox.getScene().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/AccountsViewController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport java.math.BigDecimal;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.SeparatorMenuItem;\nimport javafx.scene.control.TreeItem;\nimport javafx.scene.control.TreeTableColumn;\nimport javafx.scene.control.TreeTableRow;\nimport javafx.scene.control.TreeTableView;\nimport javafx.scene.input.MouseButton;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.RootAccount;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.IntegerTreeTableCell;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.AccountTypeFilter;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.uifx.views.AccountBalanceDisplayMode;\nimport jgnash.uifx.views.register.RegisterActions;\nimport jgnash.uifx.views.register.RegisterStage;\nimport jgnash.util.EncodeDecode;\n\n/**\n * Accounts view controller.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountsViewController implements MessageListener {\n\n    private final static String COLUMN_VISIBILITY = \"ColumnVisibility\";\n\n    private final Preferences preferences = Preferences.userNodeForPackage(AccountsViewController.class);\n\n    private final AccountTypeFilter typeFilter = new AccountTypeFilter(preferences);\n\n    private final SimpleObjectProperty<Account> selectedAccount = new SimpleObjectProperty<>();\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private TreeTableView<Account> treeTableView;\n\n    @FXML\n    private Button newButton;\n\n    @FXML\n    private Button modifyButton;\n\n    @FXML\n    private Button reconcileButton;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private Button filterButton;\n\n    @FXML\n    private Button zoomButton;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<AccountBalanceDisplayMode> accountBalanceDisplayModeChangeListener;\n\n    @FXML\n    private void initialize() {\n        deleteButton.setDisable(true);\n        reconcileButton.setDisable(true);\n\n        initializeTreeTableView();\n\n        JavaFXUtils.runLater(this::loadAccountTree);\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM, MessageChannel.ACCOUNT,\n                MessageChannel.TRANSACTION);\n\n        // Register invalidation listeners to force a reload\n        typeFilter.addListener(observable -> reload());\n\n        selectedAccount.addListener((observable, oldValue, newValue) -> updateButtonStates());\n\n        modifyButton.disableProperty().bind(selectedAccount.isNull());\n\n        accountBalanceDisplayModeChangeListener = (observable, oldValue, newValue) -> treeTableView.refresh();\n\n        AccountBalanceDisplayManager.accountBalanceDisplayMode()\n                .addListener(new WeakChangeListener<>(accountBalanceDisplayModeChangeListener));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void initializeTreeTableView() {\n        treeTableView.setShowRoot(false);   // don't show the root\n        treeTableView.setEditable(true);    // required for editable columns\n        treeTableView.setTableMenuButtonVisible(true);\n\n        treeTableView.setRowFactory(ttv -> getTreeTableRow());\n\n        // force resize policy for better default appearance\n        treeTableView.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);\n\n        // hide the horizontal scrollbar and prevent ghosting\n        treeTableView.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS);\n\n        final TreeTableColumn<Account, String> nameColumn = new TreeTableColumn<>(resources.getString(\"Column.Account\"));\n        nameColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getValue().getName()));\n\n        final TreeTableColumn<Account, Integer> entriesColumn = new TreeTableColumn<>(resources.getString(\"Column.Entries\"));\n        entriesColumn.setCellValueFactory(param -> new SimpleIntegerProperty(param.getValue().getValue().getTransactionCount()).asObject());\n\n        final TreeTableColumn<Account, BigDecimal> balanceColumn = new TreeTableColumn<>(resources.getString(\"Column.Balance\"));\n        balanceColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(AccountBalanceDisplayManager.\n                convertToSelectedBalanceMode(param.getValue().getValue().getAccountType(),\n                        param.getValue().getValue().getTreeBalance())));\n        balanceColumn.setCellFactory(cell -> new AccountCommodityFormatTreeTableCell());\n\n        final TreeTableColumn<Account, BigDecimal> reconciledBalanceColumn = new TreeTableColumn<>(resources.getString(\"Column.ReconciledBalance\"));\n        reconciledBalanceColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(AccountBalanceDisplayManager.\n                convertToSelectedBalanceMode(param.getValue().getValue().getAccountType(),\n                        param.getValue().getValue().getReconciledTreeBalance())));\n        reconciledBalanceColumn.setCellFactory(cell -> new AccountCommodityFormatTreeTableCell());\n\n        final TreeTableColumn<Account, String> currencyColumn = new TreeTableColumn<>(resources.getString(\"Column.Currency\"));\n        currencyColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getValue().getCurrencyNode().getSymbol()));\n\n        final TreeTableColumn<Account, String> typeColumn = new TreeTableColumn<>(resources.getString(\"Column.Type\"));\n        typeColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getValue().getAccountType().toString()));\n\n        final TreeTableColumn<Account, Integer> codeColumn = new TreeTableColumn<>(resources.getString(\"Column.Code\"));\n        codeColumn.setEditable(true);\n        codeColumn.setCellValueFactory(param -> new SimpleIntegerProperty(param.getValue().getValue().getAccountCode()).asObject());\n        codeColumn.setCellFactory(param -> new IntegerTreeTableCell<>());\n        codeColumn.setOnEditCommit(event -> updateAccountCode(event.getRowValue().getValue(), event.getNewValue()));\n\n        treeTableView.getColumns().addAll(nameColumn, codeColumn, entriesColumn, balanceColumn,\n                reconciledBalanceColumn, currencyColumn, typeColumn);\n\n        restoreColumnVisibility();\n\n        installListeners();\n    }\n\n    private void installListeners() {\n        treeTableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                selectedAccount.set(newValue.getValue());\n            } else {\n                selectedAccount.set(null);\n            }\n        });\n\n        for (final TreeTableColumn<?, ?> treeTableColumn : treeTableView.getColumns()) {\n            treeTableColumn.visibleProperty().addListener((observable, oldValue, newValue) -> saveColumnVisibility());\n        }\n    }\n\n    private TreeTableRow<Account> getTreeTableRow() {\n        final TreeTableRow<Account> treeTableRow = new TreeTableRow<>();\n\n        // double click handler\n        treeTableRow.setOnMouseClicked(event -> {\n            if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {\n                if (selectedAccount.get() != null && !selectedAccount.get().isPlaceHolder()) {\n                    JavaFXUtils.runLater(AccountsViewController.this::handleZoomAccountAction);\n                }\n            }\n        });\n\n        final ContextMenu rowMenu = new ContextMenu();\n\n        final MenuItem newItem = new MenuItem(resources.getString(\"Menu.New.Name\"));\n        newItem.setOnAction(event -> handleNewAccountAction());\n\n        final MenuItem modifyItem = new MenuItem(resources.getString(\"Menu.Modify.Name\"));\n        modifyItem.setOnAction(event -> handleModifyAccountAction());\n\n        final MenuItem deleteItem = new MenuItem(resources.getString(\"Menu.Delete.Name\"));\n        deleteItem.setOnAction(event -> handleDeleteAccountAction());\n        deleteItem.disableProperty().bind(deleteButton.disabledProperty());\n\n        final MenuItem visibilityMenuItem = new MenuItem(resources.getString(\"Menu.Hide.Name\"));\n        visibilityMenuItem.setOnAction(event -> handleModifyAccountAction());\n        visibilityMenuItem.setOnAction(event -> new Thread(() -> {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            if (engine != null) {\n                engine.toggleAccountVisibility(selectedAccount.get());\n            }\n        }).start());\n\n        final MenuItem reconcileItem = new MenuItem(resources.getString(\"Menu.Reconcile.Name\"));\n        reconcileItem.setOnAction(event -> handleReconcileAction());\n\n        rowMenu.getItems().addAll(newItem, modifyItem, deleteItem, new SeparatorMenuItem(), visibilityMenuItem,\n                new SeparatorMenuItem(), reconcileItem);\n\n        rowMenu.setOnShowing(event -> visibilityMenuItem.setText(selectedAccount.get().isVisible()\n                ? resources.getString(\"Menu.Hide.Name\") : resources.getString(\"Menu.Show.Name\")));\n\n        treeTableRow.contextMenuProperty().bind(\n                Bindings.when(Bindings.isNotNull(treeTableRow.itemProperty()))\n                        .then(rowMenu)\n                        .otherwise((ContextMenu) null));\n\n        return treeTableRow;\n    }\n\n    private void updateButtonStates() {\n        JavaFXUtils.runLater(() -> {\n            final Account account = selectedAccount.get();\n\n            if (account != null) {\n                final int count = account.getTransactionCount();\n\n                deleteButton.setDisable(count > 0 || account.getChildCount() > 0);\n                reconcileButton.setDisable(count <= 0);\n                zoomButton.setDisable(account.isPlaceHolder());\n            } else {\n                deleteButton.setDisable(true);\n                reconcileButton.setDisable(true);\n                zoomButton.setDisable(true);\n            }\n        });\n    }\n\n    private void saveColumnVisibility() {\n        final boolean[] columnVisibility = new boolean[treeTableView.getColumns().size()];\n\n        for (int i = 0; i < columnVisibility.length; i++) {\n            columnVisibility[i] = treeTableView.getColumns().get(i).isVisible();\n        }\n\n        preferences.put(COLUMN_VISIBILITY, EncodeDecode.encodeBooleanArray(columnVisibility));\n    }\n\n    private void restoreColumnVisibility() {\n        final String result = preferences.get(COLUMN_VISIBILITY, null);\n\n        if (result != null) {\n            boolean[] columnVisibility = EncodeDecode.decodeBooleanArray(result);\n\n            if (columnVisibility.length == treeTableView.getColumns().size()) {\n                for (int i = 0; i < columnVisibility.length; i++) {\n                    treeTableView.getColumns().get(i).setVisible(columnVisibility[i]);\n                }\n            }\n        }\n    }\n\n    @FXML\n    private void handleFilterAccountAction() {\n        StaticAccountsMethods.showAccountFilterDialog(typeFilter);\n    }\n\n    @FXML\n    private void handleModifyAccountAction() {\n        if (selectedAccount.get() != null) {\n            StaticAccountsMethods.showModifyAccountProperties(selectedAccount.get());\n        }\n    }\n\n    @FXML\n    private void handleNewAccountAction() {\n        StaticAccountsMethods.showNewAccountPropertiesDialog(selectedAccount.get());\n    }\n\n    @FXML\n    private void handleDeleteAccountAction() {\n        new Thread(() -> {   // push off the platform thread to improve performance\n            if (selectedAccount.get() != null) {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                if (!engine.removeAccount(selectedAccount.get())) {\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.AccountRemove\"));\n                }\n            }\n        }).start();\n    }\n\n    @FXML\n    private void handleZoomAccountAction() {\n        RegisterStage.getRegisterStage(selectedAccount.get()).show();\n    }\n\n    private void updateAccountCode(final Account account, final Integer code) {\n        new Thread(() -> {  // push the change to an external thread to unload the platform thread\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            Objects.requireNonNull(engine);\n\n            if (!engine.setAccountCode(account, code)) {\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.AccountUpdate\"));\n            }\n        }).start();\n    }\n\n    private void loadAccountTree() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            final RootAccount r = engine.getRootAccount();\n\n            final TreeItem<Account> root = new TreeItem<>(r);\n            root.setExpanded(true);\n\n            treeTableView.setRoot(root);\n            loadChildren(root);\n        } else {\n            treeTableView.setRoot(null);\n        }\n    }\n\n    private synchronized void loadChildren(final TreeItem<Account> parentItem) {\n        parentItem.getValue().getChildren(Comparators.getAccountByCode()).stream().filter(typeFilter::isAccountVisible).forEach(child -> {\n            TreeItem<Account> childItem = new TreeItem<>(child);\n            childItem.setExpanded(true);\n\n            parentItem.getChildren().add(childItem);\n\n            if (child.getChildCount() > 0) {\n                loadChildren(childItem);\n            }\n        });\n    }\n\n    private synchronized void reload() {\n        JavaFXUtils.runLater(this::loadAccountTree);\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n\n        switch (event.getEvent()) {\n            case ACCOUNT_ADD:\n            case ACCOUNT_MODIFY:\n            case ACCOUNT_REMOVE:\n            case ACCOUNT_VISIBILITY_CHANGE:\n                reload();\n                break;\n            case TRANSACTION_ADD:\n            case TRANSACTION_REMOVE:\n                JavaFXUtils.runLater(() -> treeTableView.refresh());\n                break;\n            case FILE_CLOSING:\n                JavaFXUtils.runLater(() -> treeTableView.setRoot(null));\n                MessageBus.getInstance().unregisterListener(this, MessageChannel.SYSTEM, MessageChannel.ACCOUNT,\n                        MessageChannel.TRANSACTION);\n                break;\n            default:\n                break;\n        }\n    }\n\n    @FXML\n    private void handleReconcileAction() {\n        RegisterActions.reconcileAccountAction(selectedAccount.get());\n    }\n\n    @FXML\n    private void handleExport() {\n        StaticAccountsMethods.exportAccountTree();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/SelectAccountController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.TreeView;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.uifx.control.AbstractAccountTreeController;\nimport jgnash.util.NotNull;\n\n/**\n * Controller for selecting an account from a tree.\n *\n * @author Craig Cavanaugh\n */\npublic class SelectAccountController extends AbstractAccountTreeController {\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private TreeView<Account> treeView;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    private Account selectedAccount = null;\n\n    @Override\n    protected TreeView<Account> getTreeView() {\n        return treeView;\n    }\n\n    @Override\n    protected boolean isAccountVisible(final Account account) {\n        return true;\n    }\n\n    @Override\n    protected boolean isAccountSelectable(final Account account) {\n        return true;\n    }\n\n    @FXML\n    public void initialize() {\n        super.initialize();\n\n        getTreeView().setShowRoot(true);\n\n        // Create and add the ok and cancel buttons to the button bar\n        final Button okButton = new Button(resources.getString(\"Button.Ok\"));\n        final Button cancelButton = new Button(resources.getString(\"Button.Cancel\"));\n\n        ButtonBar.setButtonData(okButton, ButtonBar.ButtonData.OK_DONE);\n        ButtonBar.setButtonData(cancelButton, ButtonBar.ButtonData.CANCEL_CLOSE);\n\n        buttonBar.getButtons().addAll(okButton, cancelButton);\n\n        okButton.setOnAction(event -> ((Stage) okButton.getScene().getWindow()).close());\n\n        cancelButton.setOnAction(event -> {\n            selectedAccount = null;  // clear selections\n            ((Stage) cancelButton.getScene().getWindow()).close();\n        });\n\n        getSelectedAccountProperty().addListener((observable, oldValue, newValue) -> selectedAccount = newValue);\n    }\n\n    @NotNull\n    public Optional<Account> getSelectedAccount() {\n        return Optional.ofNullable(selectedAccount);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/SelectAccountSecuritiesDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.collections.FXCollections;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.layout.ColumnConstraints;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.Priority;\nimport javafx.scene.layout.RowConstraints;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.SecurityNode;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.control.LockedCommodityListCell;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.LockedCommodityNode;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Dialog for selecting allowed account securities from a list.  If a security is used within the account, selection\n * will be forced.\n *\n * @author Craig Cavanaugh\n */\npublic class SelectAccountSecuritiesDialog {\n\n    private final ResourceBundle resources = ResourceUtils.getBundle();\n\n    private Button moveToTarget;\n    private Button moveToSource;\n\n    private final ListView<LockedCommodityNode<SecurityNode>> sourceListView = new ListView<>();\n    private final ListView<LockedCommodityNode<SecurityNode>> targetListView = new ListView<>();\n\n    private boolean result;\n    \n    public SelectAccountSecuritiesDialog(@Nullable final Account account, @NotNull Set<SecurityNode> preSelected) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        sourceListView.setCellFactory(param -> new LockedCommodityListCell<>());\n        targetListView.setCellFactory(param -> new LockedCommodityListCell<>());\n\n        Set<SecurityNode> usedSecurities = new HashSet<>();\n        Set<SecurityNode> selectedSecurityNodes = new HashSet<>();\n\n        if (account != null) {\n            usedSecurities = account.getUsedSecurities();\n            selectedSecurityNodes = account.getSecurities();\n        }\n\n        // Add the preselected set\n        selectedSecurityNodes.addAll(preSelected);\n\n        for (final SecurityNode securityNode : selectedSecurityNodes) {\n            if (usedSecurities.contains(securityNode)) {\n                targetListView.getItems().add(new LockedCommodityNode<>(securityNode, true));\n            } else {\n                targetListView.getItems().add(new LockedCommodityNode<>(securityNode, false));\n            }\n        }\n\n        for (final SecurityNode securityNode : engine.getSecurities()) {\n            if (!selectedSecurityNodes.contains(securityNode)) {\n                sourceListView.getItems().add(new LockedCommodityNode<>(securityNode, false));\n            }\n        }\n\n        FXCollections.sort(sourceListView.getItems());\n        FXCollections.sort(targetListView.getItems());\n    }\n\n    public boolean showAndWait() {\n\n        // Create the base dialog\n        final Stage dialog = new Stage(StageStyle.DECORATED);\n        dialog.initModality(Modality.APPLICATION_MODAL);\n        dialog.initOwner(MainView.getPrimaryStage());\n        dialog.setTitle(resources.getString(\"Title.AccountSecurities\"));\n\n        final Label availableLabel = new Label(resources.getString(\"Title.Available\"));\n        availableLabel.getStyleClass().add(StyleClass.LIST_TITLE_STYLE);\n\n        final Label currentLabel = new Label(resources.getString(\"Title.Current\"));\n        currentLabel.getStyleClass().add(StyleClass.LIST_TITLE_STYLE);\n\n        final GridPane gridPane = createGridPane();\n        gridPane.add(availableLabel, 0, 0);\n        gridPane.add(sourceListView, 0, 1);\n        gridPane.add(createButtonBox(), 1, 1);\n        gridPane.add(currentLabel, 2, 0);\n        gridPane.add(targetListView, 2, 1);\n        gridPane.add(createButtonBar(), 0, 2, 3, 1);\n\n        dialog.setScene(new Scene(gridPane));\n        ThemeManager.applyStyleSheets(dialog.getScene());\n        dialog.getScene().getRoot().styleProperty().bind(ThemeManager.styleProperty());\n        dialog.getScene().getRoot().getStyleClass().addAll(\"form\", \"dialog\");\n\n        StageUtils.addBoundsListener(dialog, this.getClass(), MainView.getPrimaryStage());\n\n        dialog.showAndWait();\n\n        return result;\n    }\n\n    private ButtonBar createButtonBar() {\n        final ButtonBar buttonBar = new ButtonBar();\n\n        // Create and add the ok and cancel buttons to the button bar\n        final Button okButton = new Button(resources.getString(\"Button.Ok\"));\n        final Button cancelButton = new Button(resources.getString(\"Button.Cancel\"));\n\n        ButtonBar.setButtonData(okButton, ButtonBar.ButtonData.OK_DONE);\n        ButtonBar.setButtonData(cancelButton, ButtonBar.ButtonData.CANCEL_CLOSE);\n\n        buttonBar.getButtons().addAll(okButton, cancelButton);\n\n        okButton.setOnAction(event -> {\n            result = true;\n            ((Stage) okButton.getScene().getWindow()).close();\n        });\n\n        cancelButton.setOnAction(event -> {\n            result = false;\n            ((Stage) cancelButton.getScene().getWindow()).close();\n        });\n\n        return buttonBar;\n    }\n\n    private VBox createButtonBox() {\n        final VBox vBox = new VBox();\n        vBox.setFillWidth(true);\n        vBox.getStyleClass().add(\"form\");\n\n        moveToTarget = new Button(\"\", new MaterialDesignLabel(MaterialDesignLabel.MDIcon.CHEVRON_RIGHT));\n        moveToTarget.getStyleClass().add(StyleClass.LIST_BUTTON_STYLE);\n\n        moveToSource = new Button(\"\", new MaterialDesignLabel(MaterialDesignLabel.MDIcon.CHEVRON_LEFT));\n        moveToSource.getStyleClass().add(StyleClass.LIST_BUTTON_STYLE);\n\n        moveToTarget.setMaxWidth(Double.MAX_VALUE);\n        moveToSource.setMaxWidth(Double.MAX_VALUE);\n\n        sourceListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n        targetListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        sourceListView.selectionModelProperty().addListener(listener -> bindMoveButtonsToListItems());\n        targetListView.selectionModelProperty().addListener(listener -> bindMoveButtonsToListItems());\n\n        bindMoveButtonsToListItems();\n\n        moveToTarget.setOnAction(event -> moveItemsToTarget());\n        moveToSource.setOnAction(event -> moveItemsToSource());\n\n        vBox.getChildren().addAll(moveToTarget, moveToSource);\n\n        return vBox;\n    }\n\n    private GridPane createGridPane() {\n        final GridPane gridPane = new GridPane();\n        gridPane.getStyleClass().add(\"form\");\n\n        final ColumnConstraints col0 = new ColumnConstraints();\n        col0.setFillWidth(true);\n        col0.setHgrow(Priority.ALWAYS);\n        col0.setMaxWidth(Double.MAX_VALUE);\n        col0.setPrefWidth(220);\n\n        final ColumnConstraints col1 = new ColumnConstraints();\n        col1.setFillWidth(true);\n        col1.setHgrow(Priority.NEVER);\n\n        final ColumnConstraints col2 = new ColumnConstraints();\n        col2.setFillWidth(true);\n        col2.setHgrow(Priority.ALWAYS);\n        col2.setMaxWidth(Double.MAX_VALUE);\n        col2.setPrefWidth(220);\n\n        gridPane.getColumnConstraints().addAll(col0, col1, col2);\n\n        final RowConstraints row0 = new RowConstraints();\n        row0.setFillHeight(true);\n        row0.setVgrow(Priority.NEVER);\n\n        final RowConstraints row1 = new RowConstraints();\n        row1.setMaxHeight(Double.MAX_VALUE);\n        row1.setPrefHeight(220);\n        row1.setVgrow(Priority.ALWAYS);\n\n        final RowConstraints row2 = new RowConstraints();\n        row0.setFillHeight(true);\n        row0.setVgrow(Priority.NEVER);\n\n        gridPane.getRowConstraints().addAll(row0, row1, row2);\n\n        return gridPane;\n    }\n\n    private void bindMoveButtonsToListItems() {\n        moveToTarget.disableProperty().bind(Bindings.isEmpty(sourceListView.getItems()));\n        moveToSource.disableProperty().bind(Bindings.isEmpty(targetListView.getItems()));\n    }\n\n    synchronized private void moveItems(final ListView<LockedCommodityNode<SecurityNode>> sourceView,\n                                        final ListView<LockedCommodityNode<SecurityNode>> destinationView) {\n        final List<LockedCommodityNode<SecurityNode>> selectedItems\n                = new ArrayList<>(sourceView.getSelectionModel().getSelectedItems());\n\n        // filter out any locked items\n        selectedItems.removeIf(LockedCommodityNode::isLocked);\n\n        moveItems(sourceView, destinationView, selectedItems);\n    }\n\n    synchronized private void moveItems(final ListView<LockedCommodityNode<SecurityNode>> sourceView,\n                                        final ListView<LockedCommodityNode<SecurityNode>> destinationView,\n                                        final List<LockedCommodityNode<SecurityNode>> items) {\n        for (final LockedCommodityNode<SecurityNode> item : items) {\n            sourceView.getItems().remove(item);\n            destinationView.getItems().add(item);\n\n            // Sort the destination list\n            FXCollections.sort(destinationView.getItems());\n        }\n    }\n\n    synchronized private void moveItemsToTarget() {\n        moveItems(sourceListView, targetListView);\n        sourceListView.getSelectionModel().clearSelection();\n    }\n\n    synchronized private void moveItemsToSource() {\n        moveItems(targetListView, sourceListView);\n        targetListView.getSelectionModel().clearSelection();\n    }\n\n    public Set<SecurityNode> getSelectedSecurities() {\n        return targetListView.getItems().stream().map(LockedCommodityNode::getNode)\n                .collect(Collectors.toCollection(TreeSet::new));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/accounts/StaticAccountsMethods.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.accounts;\n\nimport java.io.File;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.stage.FileChooser;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\nimport jgnash.convert.exportantur.csv.CsvExport;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.report.poi.Workbook;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.report.ListOfAccountsReport;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.AccountTypeFilter;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.FileUtils;\nimport jgnash.util.Nullable;\n\n/**\n * Static support methods for Account manipulation.\n *\n * @author Craig Cavanaugh\n */\npublic final class StaticAccountsMethods {\n\n    private static final String EXPORT_DIR = \"exportDir\";\n\n    private static final String XLS = \"xls\";\n\n    private StaticAccountsMethods() {\n        // Utility class\n    }\n\n    public static void showAccountFilterDialog(final AccountTypeFilter accountTypeFilter) {\n        final Stage dialog = new Stage(StageStyle.DECORATED);\n        dialog.initModality(Modality.APPLICATION_MODAL);\n        dialog.initOwner(MainView.getPrimaryStage());\n        dialog.setTitle(ResourceUtils.getString(\"Title.VisibleAccountTypes\"));\n\n        final AccountTypeFilterFormController controller = FXMLUtils.loadFXML(o -> dialog.setScene(new Scene((Parent) o)),\n                \"AccountTypeFilterForm.fxml\", ResourceUtils.getBundle());\n\n        ThemeManager.applyStyleSheets(dialog.getScene());\n\n        controller.setAccountTypeFilter(accountTypeFilter);\n\n        dialog.setResizable(false);\n\n        StageUtils.addBoundsListener(dialog, AccountTypeFilterFormController.class, MainView.getPrimaryStage());\n\n        dialog.showAndWait();\n    }\n\n    static void showNewAccountPropertiesDialog(@Nullable final Account parentAccount) {\n        final Stage dialog = new Stage(StageStyle.DECORATED);\n        dialog.initModality(Modality.APPLICATION_MODAL);\n        dialog.initOwner(MainView.getPrimaryStage());\n        dialog.setTitle(ResourceUtils.getString(\"Title.NewAccount\"));\n\n        final AccountPropertiesController controller = FXMLUtils.loadFXML(o -> dialog.setScene(new Scene((Parent) o)),\n                \"AccountProperties.fxml\", ResourceUtils.getBundle());\n\n        ThemeManager.applyStyleSheets(dialog.getScene());\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        controller.setSelectedCurrency(engine.getDefaultCurrency());\n\n        controller.setParentAccount(parentAccount != null ? parentAccount : engine.getRootAccount());\n\n        dialog.setResizable(false);\n\n        StageUtils.addBoundsListener(dialog, AccountPropertiesController.class, MainView.getPrimaryStage());\n\n        dialog.showAndWait();\n\n        if (controller.getResult()) {\n            Account account = controller.getTemplate();\n\n            engine.addAccount(account.getParent(), account);\n\n            if (account.getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                if (!engine.updateAccountSecurities(account, controller.getSecurityNodes())) {\n                    StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.SecurityAccountUpdate\"));\n                }\n            }\n        }\n    }\n\n    static void showModifyAccountProperties(final Account account) {\n        final Stage dialog = new Stage(StageStyle.DECORATED);\n        dialog.initModality(Modality.WINDOW_MODAL);\n        dialog.initOwner(MainView.getPrimaryStage());\n        dialog.setTitle(ResourceUtils.getString(\"Title.ModifyAccount\"));\n\n        final AccountPropertiesController controller = FXMLUtils.loadFXML(o -> dialog.setScene(new Scene((Parent) o)),\n                \"AccountProperties.fxml\", ResourceUtils.getBundle());\n\n        ThemeManager.applyStyleSheets(dialog.getScene());\n\n        controller.loadProperties(account);\n\n        dialog.setResizable(false);\n\n        StageUtils.addBoundsListener(dialog, AccountPropertiesController.class, MainView.getPrimaryStage());\n\n        dialog.showAndWait();\n\n        if (controller.getResult()) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            Account template = controller.getTemplate();\n\n            if (!engine.modifyAccount(template, account)) {\n                StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.AccountUpdate\"));\n            }\n\n            if (account.getAccountType().getAccountGroup() == AccountGroup.INVEST) {\n                if (!engine.updateAccountSecurities(account, controller.getSecurityNodes())) {\n                    StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.SecurityAccountUpdate\"));\n                }\n            }\n        }\n    }\n\n    public static Optional<Account> selectAccount(@Nullable final Account parentAccount, @Nullable final Account... excluded) {\n        final FXMLUtils.Pair<SelectAccountController> pair =\n                FXMLUtils.load(SelectAccountController.class.getResource(\"SelectAccountForm.fxml\"),\n                        ResourceUtils.getString(\"Title.ParentAccount\"));\n\n        if (parentAccount != null) {\n            pair.getController().setSelectedAccount(parentAccount);\n        }\n\n        // add excluded accounts if any\n        pair.getController().addExcludeAccounts(excluded);\n\n        pair.getStage().showAndWait();\n\n        return pair.getController().getSelectedAccount();\n    }\n\n    static void exportAccountTree() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final Preferences pref = Preferences.userNodeForPackage(StaticAccountsMethods.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(EXPORT_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.CsvFiles\") + \" (*.csv)\", \"*.csv\"),\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.SpreadsheetFiles\") + \" (*.xls)\",\n                        \"*.xls\"),\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.SpreadsheetFiles\") + \" (*.xlsx)\",\n                        \"*.xlsx\")\n        );\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n        final File exportFile;\n\n        if (file != null) {\n            if (!FileUtils.fileHasExtension(file.getName())) {  // fix up the file name if the user did not specify it\n                final String fileExtension = fileChooser.getSelectedExtensionFilter().getExtensions().get(0).substring(1);\n                exportFile = new File(FileUtils.stripFileExtension(file.getAbsolutePath()) + fileExtension);\n            } else {\n                exportFile = file;\n            }\n\n            pref.put(EXPORT_DIR, exportFile.getParentFile().getAbsolutePath());\n\n            final Task<Void> exportTask = new Task<>() {\n                @Override\n                protected Void call() {\n                    updateMessage(resources.getString(\"Message.PleaseWait\"));\n                    updateProgress(-1, Long.MAX_VALUE);\n\n                    if (FileUtils.getFileExtension(exportFile.getName()).contains(XLS)) {\n\n                        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n                        Objects.requireNonNull(engine);\n\n                        final AbstractReportTableModel reportTableModel =\n                                new ListOfAccountsReport.AccountListModel(engine.getAccountList(), engine.getDefaultCurrency());\n\n                        Workbook.export(reportTableModel, exportFile);\n                    } else {\n                        CsvExport.exportAccountTree(EngineFactory.getEngine(EngineFactory.DEFAULT), exportFile.toPath());\n                    }\n                    return null;\n                }\n            };\n\n            new Thread(exportTask).start();\n\n            StaticUIMethods.displayTaskProgress(exportTask);\n        }\n\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/BudgetGoalsDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.math.BigDecimal;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.time.Month;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.budget.BudgetFactory;\nimport jgnash.engine.budget.BudgetGoal;\nimport jgnash.time.Period;\nimport jgnash.engine.budget.BudgetPeriodDescriptor;\nimport jgnash.engine.budget.BudgetPeriodDescriptorFactory;\nimport jgnash.engine.budget.Pattern;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.BigDecimalTableCell;\nimport jgnash.uifx.control.DecimalTextField;\n\n/**\n * Controller for budget goals.\n *\n * The startMonth property must be set before account and working year property are assigned.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetGoalsDialogController {\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private DecimalTextField fillPatternAmountDecimalTextField;\n\n    @FXML\n    private Spinner<Integer> endRowSpinner;\n\n    @FXML\n    private Spinner<Integer> startRowSpinner;\n\n    @FXML\n    private ComboBox<Pattern> patternComboBox;\n\n    @FXML\n    private DecimalTextField fillAllDecimalTextField;\n\n    @FXML\n    private TableView<BudgetPeriodDescriptor> goalTable;\n\n    @FXML\n    private Label currencyLabel;\n\n    @FXML\n    private ComboBox<Period> periodComboBox;\n\n    private BudgetGoal result = null;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final SimpleObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private final SimpleObjectProperty<BudgetGoal> budgetGoal = new SimpleObjectProperty<>();\n\n    private final IntegerProperty workingYear = new SimpleIntegerProperty();\n\n    private final IntegerProperty descriptorSize = new SimpleIntegerProperty();\n\n    private final ObjectProperty<NumberFormat> numberFormat = new SimpleObjectProperty<>(NumberFormat.getInstance());\n\n    private final SimpleObjectProperty<Month> startMonth = new SimpleObjectProperty<>();\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        endRowSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1));\n        startRowSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1));\n\n        periodComboBox.getItems().addAll(Period.values());\n\n        patternComboBox.getItems().addAll(Pattern.values());\n        patternComboBox.setValue(Pattern.EveryRow);\n\n        fillAllDecimalTextField.emptyWhenZeroProperty().set(false);\n        fillPatternAmountDecimalTextField.emptyWhenZeroProperty().set(false);\n\n        fillAllDecimalTextField.setDecimal(BigDecimal.ZERO);\n        fillPatternAmountDecimalTextField.setDecimal(BigDecimal.ZERO);\n\n        goalTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        goalTable.setEditable(true);\n\n        final TableColumn<BudgetPeriodDescriptor, String> periodColumn\n                = new TableColumn<>(resources.getString(\"Column.Period\"));\n        periodColumn.setEditable(false);\n\n        periodColumn.setCellValueFactory(param -> {\n            if (param != null) {\n                return new SimpleStringProperty(param.getValue().getPeriodDescription());\n            }\n            return new SimpleStringProperty(\"\");\n        });\n        periodColumn.setSortable(false);\n\n        goalTable.getColumns().add(periodColumn);\n\n        final TableColumn<BudgetPeriodDescriptor, BigDecimal> amountColumn\n                = new TableColumn<>(resources.getString(\"Column.Amount\"));\n        amountColumn.setEditable(true);\n        amountColumn.setSortable(false);\n\n        amountColumn.setCellValueFactory(param -> {\n            if (param != null) {\n                final BudgetPeriodDescriptor descriptor = param.getValue();\n                final BigDecimal goal\n                        = budgetGoal.get().getGoal(descriptor.getStartPeriod(), descriptor.getEndPeriod(),\n                        descriptor.getStartDate().isLeapYear());\n\n                return new SimpleObjectProperty<>(goal.setScale(accountProperty().get().getCurrencyNode().getScale(),\n                        MathConstants.roundingMode));\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        amountColumn.setCellFactory(cell -> new BigDecimalTableCell<>(numberFormat));\n\n        amountColumn.setOnEditCommit(event -> {\n            final BudgetPeriodDescriptor descriptor = event.getTableView().getItems()\n                    .get(event.getTablePosition().getRow());\n\n            budgetGoalProperty().get().setGoal(descriptor.getStartPeriod(), descriptor.getEndPeriod(),\n                    event.getNewValue(), descriptor.getStartDate().isLeapYear());\n        });\n\n        goalTable.getColumns().add(amountColumn);\n\n        periodComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                budgetGoalProperty().get().setBudgetPeriod(newValue);\n\n                final List<BudgetPeriodDescriptor> descriptors = getDescriptors();\n\n                goalTable.getItems().setAll(descriptors);\n                descriptorSize.set(descriptors.size());\n            }\n        });\n\n        budgetGoalProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                periodComboBox.setValue(newValue.getBudgetPeriod());\n            }\n        });\n\n        // the spinner factory max values do not like being bound; Set value instead\n        descriptorSize.addListener((observable, oldValue, newValue) -> {\n            ((SpinnerValueFactory.IntegerSpinnerValueFactory) endRowSpinner.getValueFactory())\n                    .setMax(newValue.intValue());\n\n            ((SpinnerValueFactory.IntegerSpinnerValueFactory) startRowSpinner.getValueFactory())\n                    .setMax(newValue.intValue());\n\n            endRowSpinner.getValueFactory().setValue(newValue.intValue());\n        });\n\n        // account has changed; update currency related properties\n        accountProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                final CurrencyNode currencyNode = newValue.getCurrencyNode();\n\n                currencyLabel.setText(currencyNode.getSymbol());\n\n                fillAllDecimalTextField.scaleProperty().set(currencyNode.getScale());\n                fillAllDecimalTextField.minScaleProperty().set(currencyNode.getScale());\n\n                fillPatternAmountDecimalTextField.scaleProperty().set(currencyNode.getScale());\n                fillPatternAmountDecimalTextField.minScaleProperty().set(currencyNode.getScale());\n\n                final NumberFormat decimalFormat = NumberFormat.getInstance();\n                if (decimalFormat instanceof DecimalFormat) {\n                    decimalFormat.setMinimumFractionDigits(currencyNode.getScale());\n                    decimalFormat.setMaximumFractionDigits(currencyNode.getScale());\n                }\n\n                numberFormat.set(decimalFormat);\n            }\n        });\n    }\n\n    public SimpleObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    /**\n     * A working clone should be set instead of the base.  This property will be modified as needed.\n     *\n     * @return BudgetGoal property\n     */\n    SimpleObjectProperty<BudgetGoal> budgetGoalProperty() {\n        return budgetGoal;\n    }\n\n    IntegerProperty workingYearProperty() {\n        return workingYear;\n    }\n\n    SimpleObjectProperty<Month> startMonthProperty() {\n        return startMonth;\n    }\n\n    public Optional<BudgetGoal> getResult() {\n        return Optional.ofNullable(result);\n    }\n\n    private List<BudgetPeriodDescriptor> getDescriptors() {\n        return BudgetPeriodDescriptorFactory.getDescriptors(workingYear.get(), startMonth.get(),\n                budgetGoal.get().getBudgetPeriod());\n    }\n\n    @FXML\n    private void handleHistoricalFill() {\n        setBudgetGoal(BudgetFactory.buildAverageBudgetGoal(account.get(), getDescriptors(), true));\n    }\n\n    @FXML\n    private void handleFillAllAction() {\n        final BigDecimal fillAmount = fillAllDecimalTextField.getDecimal();\n\n        for (final BudgetPeriodDescriptor descriptor : getDescriptors()) {\n            budgetGoal.get().setGoal(descriptor.getStartPeriod(), descriptor.getEndPeriod(), fillAmount,\n                    descriptor.getStartDate().isLeapYear());\n        }\n\n        goalTable.refresh();\n    }\n\n    @FXML\n    private void handlePatternFillAction() {\n        final BigDecimal fillAmount = fillPatternAmountDecimalTextField.getDecimal();\n\n        final int startRow = startRowSpinner.getValue() - 1;\n        final int endRow = endRowSpinner.getValue() - 1;\n\n        final Pattern pattern = patternComboBox.getValue();\n\n        setBudgetGoal(BudgetFactory.buildBudgetGoal(budgetGoal.get(), getDescriptors(), pattern, startRow,\n                endRow, fillAmount));\n    }\n\n    private void setBudgetGoal(final BudgetGoal budgetGoal) {\n        this.budgetGoal.set(budgetGoal);\n\n        final List<BudgetPeriodDescriptor> descriptors = getDescriptors();\n\n        goalTable.getItems().setAll(descriptors);\n        descriptorSize.set(descriptors.size());\n\n        goalTable.refresh();\n    }\n\n    @FXML\n    private void handleOkayAction() {\n        result = budgetGoal.get();\n        ((Stage) periodComboBox.getScene().getWindow()).close();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) periodComboBox.getScene().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/BudgetManagerDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SelectionMode;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.TextInputDialog;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for budget management.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetManagerDialogController implements MessageListener {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button duplicateButton;\n\n    @FXML\n    private Button renameButton;\n\n    @FXML\n    private ListView<Budget> budgetListView;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        deleteButton.disableProperty().bind(budgetListView.getSelectionModel().selectedItemProperty().isNull());\n        duplicateButton.disableProperty().bind(budgetListView.getSelectionModel().selectedItemProperty().isNull());\n        renameButton.disableProperty().bind(budgetListView.getSelectionModel().selectedItemProperty().isNull());\n\n        budgetListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        JavaFXUtils.runLater(this::loadBudgetListView);\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.BUDGET);\n    }\n\n    private void loadBudgetListView() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        // Create a sorted List of active budgets\n        final List<Budget> budgetList = engine.getBudgetList();\n        Collections.sort(budgetList);\n\n        budgetListView.getItems().setAll(budgetList);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        MessageBus.getInstance().unregisterListener(this, MessageChannel.BUDGET);\n        ((Stage)parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleNewAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Budget newBudget = new Budget();\n\n        String name = resources.getString(\"Word.NewBudget\");\n\n        int count = 2;\n\n        while (true) {\n            boolean nameIsUnique = true;\n\n            for (final Budget budget : engine.getBudgetList()) {\n                if (budget.getName().equals(name)) {\n                    name = resources.getString(\"Word.NewBudget\") + \" \" + count;\n                    count++;\n                    nameIsUnique = false;\n                }\n            }\n\n            if (nameIsUnique) {\n                break;\n            }\n        }\n\n        newBudget.setName(name);\n        newBudget.setDescription(resources.getString(\"Word.NewBudget\"));\n\n        if (!engine.addBudget(newBudget)) {\n            StaticUIMethods.displayError(resources.getString(\"Message.Error.NewBudget\"));\n        }\n    }\n\n    @FXML\n    private void handleNewHistoricalAction() {\n        final FXMLUtils.Pair<HistoricalBudgetDialogController> pair =\n                FXMLUtils.load(HistoricalBudgetDialogController.class.getResource(\"HistoricalBudgetDialog.fxml\"),\n                        resources.getString(\"Title.NewBudget\"));\n\n        pair.getStage().show();\n        pair.getStage().setResizable(false);\n    }\n\n    @FXML\n    private void handleDuplicateAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        for (final Budget budget : budgetListView.getSelectionModel().getSelectedItems()) {\n            try {\n                final Budget newBudget = (Budget) budget.clone();\n                if (!engine.addBudget(newBudget)) {\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.BudgetDuplicate\"));\n                }\n            } catch (final CloneNotSupportedException e) {\n                Logger.getLogger(BudgetManagerDialogController.class.getName()).log(Level.SEVERE, e.toString(), e);\n            }\n        }\n    }\n\n    @FXML\n    private void handleRenameAction() {\n        for (final Budget budget : budgetListView.getSelectionModel().getSelectedItems()) {\n\n            final TextInputDialog textInputDialog = new TextInputDialog(budget.getName());\n            textInputDialog.setTitle(resources.getString(\"Title.RenameBudget\"));\n            textInputDialog.setContentText(resources.getString(\"Label.RenameBudget\"));\n\n            final Optional<String> optional = textInputDialog.showAndWait();\n\n            optional.ifPresent(s -> {\n                if (!s.isEmpty()) {\n                    final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                    Objects.requireNonNull(engine);\n\n                    budget.setName(s);\n                    engine.updateBudget(budget);\n                }\n            });\n        }\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<Budget> selected = new ArrayList<>(budgetListView.getSelectionModel().getSelectedItems());\n\n        if (!selected.isEmpty()) {\n            final String message = selected.size() == 1 ? resources.getString(\"Message.ConfirmBudgetDelete\") :\n                                           resources.getString(\"Message.ConfirmMultipleBudgetDelete\");\n\n            if (StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.Confirm\"), message).getButtonData()\n                        == ButtonBar.ButtonData.YES) {\n                selected.stream().filter(value -> !engine.removeBudget(value)).forEach(value\n                                                                                               -> StaticUIMethods.displayError(resources.getString(\"Message.Error.BudgetRemove\")));\n            }\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        if (message.getEvent() == ChannelEvent.BUDGET_ADD || message.getEvent() == ChannelEvent.BUDGET_REMOVE\n                    || message.getEvent() == ChannelEvent.BUDGET_UPDATE) {\n            JavaFXUtils.runLater(this::loadBudgetListView);\n        }\n    }\n\n    public static void showBudgetManager() {\n        final FXMLUtils.Pair<BudgetManagerDialogController> pair =\n                FXMLUtils.load(BudgetManagerDialogController.class.getResource(\"BudgetManagerDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.BudgetManager\"));\n\n        pair.getStage().show();\n        pair.getStage().setResizable(false);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/BudgetPropertiesDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.math.RoundingMode;\nimport java.util.Objects;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ListCell;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.budget.Budget;\nimport jgnash.resource.util.MonthName;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.Period;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Controller for budget properties.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetPropertiesDialogController {\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private CheckBox incomeCheckBox;\n\n    @FXML\n    private CheckBox expenseCheckBox;\n\n    @FXML\n    private CheckBox assetCheckBox;\n\n    @FXML\n    private CheckBox liabilityCheckBox;\n\n    @FXML\n    private ComboBox<Period> periodComboBox;\n\n    @FXML\n    private TextField descriptionTextField;\n\n    @FXML\n    private ComboBox<RoundMode> roundingMethodComboBox;\n\n    @FXML\n    private Spinner<Integer> scaleSpinner;\n\n    @FXML\n    private ComboBox<MonthName> startMonthComboBox;\n\n    private Budget budget;\n\n    @FXML\n    private void initialize() {\n        periodComboBox.getItems().setAll(Period.values());\n        startMonthComboBox.getItems().setAll(MonthName.values());\n\n        Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        int maxScale = 0;   // max scale is a function of available currencies\n\n        for (final CurrencyNode currencyNode : engine.getCurrencies()) {\n            maxScale = Math.max(maxScale, currencyNode.getScale());\n        }\n\n        roundingMethodComboBox.setCellFactory(param -> new RoundModeListCell());\n\n        scaleSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(-10, maxScale, maxScale, 1));\n\n        roundingMethodComboBox.getItems().setAll(RoundMode.values());\n        roundingMethodComboBox.setValue(RoundMode.FLOOR);\n\n        startMonthComboBox.setValue(MonthName.JANUARY);\n    }\n\n    @FXML\n    public void handleOkayAction() {\n\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        boolean modified = false;\n\n        if (budget.getBudgetPeriod() != periodComboBox.getValue()) {\n            modified = true;\n            budget.setBudgetPeriod(periodComboBox.getValue());\n        }\n\n        if (!descriptionTextField.getText().isEmpty() && !budget.getDescription().equals(descriptionTextField.getText())) {\n            modified = true;\n            budget.setDescription(descriptionTextField.getText());\n        }\n\n        if (assetCheckBox.isSelected() != budget.areAssetAccountsIncluded()) {\n            modified = true;\n            budget.setAssetAccountsIncluded(assetCheckBox.isSelected());\n        }\n\n        if (incomeCheckBox.isSelected() != budget.areIncomeAccountsIncluded()) {\n            modified = true;\n            budget.setIncomeAccountsIncluded(incomeCheckBox.isSelected());\n        }\n\n        if (expenseCheckBox.isSelected() != budget.areExpenseAccountsIncluded()) {\n            modified = true;\n            budget.setExpenseAccountsIncluded(expenseCheckBox.isSelected());\n        }\n\n        if (liabilityCheckBox.isSelected() != budget.areLiabilityAccountsIncluded()) {\n            modified = true;\n            budget.setLiabilityAccountsIncluded(liabilityCheckBox.isSelected());\n        }\n\n        if (scaleSpinner.getValue().byteValue() != budget.getRoundingScale()) {\n            modified = true;\n            budget.setRoundingScale(scaleSpinner.getValue().byteValue());\n        }\n\n        if (roundingMethodComboBox.getValue().roundingMode != budget.getRoundingMode()) {\n            modified = true;\n            budget.setRoundingMode(roundingMethodComboBox.getValue().roundingMode);\n        }\n\n        if (startMonthComboBox.getValue().getMonth() != budget.getStartMonth()) {\n            modified = true;\n            budget.setStartMonth(startMonthComboBox.getValue().getMonth());\n        }\n\n        if (modified) {\n            final Thread thread = new Thread(() -> {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                engine.updateBudget(budget);\n            });\n\n            thread.start();\n        }\n\n        ((Stage) incomeCheckBox.getScene().getWindow()).close();\n    }\n\n    @FXML\n    public void handleCloseAction() {\n        ((Stage) incomeCheckBox.getScene().getWindow()).close();\n    }\n\n    void setBudget(@NotNull final Budget budget) {\n\n        Objects.requireNonNull(budget);\n\n        this.budget = budget;\n\n        JavaFXUtils.runLater(() -> {\n            descriptionTextField.setText(budget.getDescription());\n            periodComboBox.setValue(budget.getBudgetPeriod());\n\n            assetCheckBox.setSelected(budget.areAssetAccountsIncluded());\n            incomeCheckBox.setSelected(budget.areIncomeAccountsIncluded());\n            expenseCheckBox.setSelected(budget.areExpenseAccountsIncluded());\n            liabilityCheckBox.setSelected(budget.areLiabilityAccountsIncluded());\n            scaleSpinner.getValueFactory().setValue((int) budget.getRoundingScale());\n            roundingMethodComboBox.setValue(RoundMode.valueOf(budget.getRoundingMode()));\n            startMonthComboBox.setValue(MonthName.valueOf(budget.getStartMonth()));\n        });\n    }\n\n    private enum RoundMode {\n\n        CEILING(RoundingMode.CEILING, ResourceUtils.getString(\"RoundingMode.Ceiling.Name\"), ResourceUtils.getString(\"RoundingMode.Ceiling.Description\")),\n        DOWN(RoundingMode.DOWN, ResourceUtils.getString(\"RoundingMode.Down.Name\"), ResourceUtils.getString(\"RoundingMode.Down.Description\")),\n        HALF_DOWN(RoundingMode.HALF_DOWN, ResourceUtils.getString(\"RoundingMode.HalfDown.Name\"), ResourceUtils.getString(\"RoundingMode.HalfDown.Description\")),\n        HALF_EVEN(RoundingMode.HALF_EVEN, ResourceUtils.getString(\"RoundingMode.HalfEven.Name\"), ResourceUtils.getString(\"RoundingMode.HalfEven.Description\")),\n        HALF_UP(RoundingMode.HALF_UP, ResourceUtils.getString(\"RoundingMode.HalfUp.Name\"), ResourceUtils.getString(\"RoundingMode.HalfUp.Description\")),\n        FLOOR(RoundingMode.FLOOR, ResourceUtils.getString(\"RoundingMode.Floor.Name\"), ResourceUtils.getString(\"RoundingMode.Floor.Description\")),\n        UP(RoundingMode.UP, ResourceUtils.getString(\"RoundingMode.Up.Name\"), ResourceUtils.getString(\"RoundingMode.Up.Description\"));\n\n        private final RoundingMode roundingMode;\n        private final String name;\n        private final String description;\n\n        RoundMode(final RoundingMode roundingMode, final String name, final String description) {\n            this.roundingMode = roundingMode;\n            this.description = description;\n            this.name = name;\n        }\n\n        @Override\n        public String toString() {\n            return name;\n        }\n\n        public static RoundMode valueOf(final RoundingMode roundingMode) {\n            switch (roundingMode) {\n                case CEILING:\n                    return RoundMode.CEILING;\n                case DOWN:\n                    return RoundMode.DOWN;\n                case HALF_DOWN:\n                    return RoundMode.HALF_DOWN;\n                case HALF_EVEN:\n                    return RoundMode.HALF_EVEN;\n                case HALF_UP:\n                    return RoundMode.HALF_UP;\n                case FLOOR:\n                    return RoundMode.FLOOR;\n                case UP:\n                    return RoundMode.UP;\n                default:\n                    throw new IllegalArgumentException(\"argument out of range\");\n            }\n        }\n    }\n\n    private static class RoundModeListCell extends ListCell<RoundMode> {\n\n        @Override\n        public void updateItem(final RoundMode item, final boolean empty) {\n\n            super.updateItem(item, empty);  // required\n\n            if (!empty && item != null) {\n                setText(item.name);\n                setTooltip(new Tooltip(item.description));\n            } else {\n                setText(\"\");\n                setTooltip(null);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/BudgetSparkLine.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.math.BigDecimal;\nimport java.util.Collections;\nimport java.util.List;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.scene.canvas.Canvas;\nimport javafx.scene.canvas.GraphicsContext;\nimport javafx.scene.paint.Color;\n\nimport jgnash.uifx.skin.ThemeManager;\n\n/**\n * Spark line Canvas for budgets.\n *\n * @author Craig Cavanaugh\n */\nclass BudgetSparkLine extends Canvas {\n\n    private static final double DEFAULT_WIDTH = 180;\n\n    private static final double MAX_WIDTH = 400;\n\n    private static final double DEFAULT_PERIOD_GAP = 1.0;\n\n    private static final double DEFAULT_BAR_WIDTH = 2.0;\n\n    private static final double MARGIN = 6.0;\n\n    private static final double HEIGHT_MULTIPLIER = 1.5;\n\n    private final ObservableList<BigDecimal> amounts = FXCollections.observableArrayList();\n\n    private double barWidth = DEFAULT_BAR_WIDTH;\n\n    private double periodGap = DEFAULT_PERIOD_GAP;\n\n    /**\n     * Listens for changes to the font scale\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<Number> fontScaleListener;\n\n    BudgetSparkLine(final List<BigDecimal> amounts) {\n\n        fontScaleListener = (observable, oldValue, newValue) -> draw();\n        ThemeManager.fontScaleProperty().addListener(new WeakChangeListener<>(fontScaleListener));\n\n        setAmounts(amounts);\n    }\n\n    private void setAmounts(final List<BigDecimal> amounts) {\n        this.amounts.clear();\n        this.amounts.addAll(amounts);\n\n        draw();\n    }\n\n    private double calculateWidth() {\n        return (amounts.size() * barWidth) + (amounts.size() * periodGap) + (MARGIN * 2) + barWidth;\n    }\n\n    private void draw() {\n        if (calculateWidth() < DEFAULT_WIDTH || calculateWidth() > MAX_WIDTH) {\n            final double scale = DEFAULT_WIDTH / calculateWidth();\n            barWidth = barWidth * scale;\n            periodGap = periodGap * scale;\n        }\n\n        setWidth(calculateWidth());\n        setHeight(ThemeManager.getBaseTextHeight() * HEIGHT_MULTIPLIER);\n\n        final GraphicsContext gc = getGraphicsContext2D();\n        gc.clearRect(0, 0, getWidth(), getHeight());\n        gc.setLineWidth(barWidth);\n\n        final double centerLine = getHeight() / 2d;\n        final double max = Collections.max(amounts).doubleValue();\n        final double min = Collections.min(amounts).doubleValue();\n        final double scale = centerLine / Math.max(max, Math.abs(min));\n\n        double x = MARGIN + barWidth / 2d;\n\n        for (final BigDecimal amount : amounts) {\n            if (amount.signum() > 0) {\n                gc.setStroke(Color.BLACK);\n                gc.strokeLine(x, centerLine - (barWidth / 2d), x, centerLine - amount.abs().doubleValue() * scale);\n            } else if (amount.signum() < 0) {\n                gc.setStroke(Color.RED);\n                gc.strokeLine(x, centerLine + (barWidth / 2d), x, centerLine + amount.abs().doubleValue() * scale);\n            }\n\n            x += (barWidth + periodGap);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/BudgetTableController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\nimport java.util.stream.Collectors;\n\nimport javafx.application.Platform;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.geometry.Point2D;\nimport javafx.geometry.Pos;\nimport javafx.scene.Cursor;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.ScrollBar;\nimport javafx.scene.control.Spinner;\nimport javafx.scene.control.SpinnerValueFactory;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.TreeItem;\nimport javafx.scene.control.TreeTableCell;\nimport javafx.scene.control.TreeTableColumn;\nimport javafx.scene.control.TreeTableView;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.budget.BudgetGoal;\nimport jgnash.engine.budget.BudgetPeriodDescriptor;\nimport jgnash.engine.budget.BudgetPeriodResults;\nimport jgnash.engine.budget.BudgetResultsModel;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.Period;\nimport jgnash.uifx.control.NullTableViewSelectionModel;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.NotNull;\n\nimport static jgnash.uifx.skin.StyleClass.BOLD_LABEL_ID;\nimport static jgnash.uifx.skin.StyleClass.BOLD_NEGATIVE_LABEL_ID;\nimport static jgnash.uifx.skin.StyleClass.NORMAL_CELL_ID;\nimport static jgnash.uifx.skin.StyleClass.NORMAL_NEGATIVE_CELL_ID;\nimport static jgnash.uifx.skin.StyleClass.TODAY_BOLD_LABEL_ID;\nimport static jgnash.uifx.skin.StyleClass.TODAY_BOLD_NEGATIVE_LABEL_ID;\nimport static jgnash.uifx.skin.StyleClass.TODAY_NORMAL_CELL_ID;\nimport static jgnash.uifx.skin.StyleClass.TODAY_NORMAL_NEGATIVE_CELL_ID;\n\n/**\n * Controller for budget tables.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetTableController implements MessageListener {\n\n    private static final String RUNNING_TOTALS = \"runningTotals\";\n\n    private static final String ACCOUNT_COLUMN_WIDTH = \"accountColumnWidth\";\n\n    private static final int ROW_HEIGHT_MULTIPLIER = 2;\n\n    //TODO: Magic number that needs to be fixed or controlled with css\n    private static final double BORDER_MARGIN = 2;\n\n    /**\n     * Limits the selection span of +/- the specified number of years\n     */\n    private static final int YEAR_MARGIN = 15;\n\n    // Initial column width\n    private static final double INITIAL_WIDTH = 55;\n\n    private static final String NOW = \"now\";\n\n    private final Preferences preferences = Preferences.userNodeForPackage(BudgetTableController.class);\n\n    @FXML\n    private CheckBox runningTotalsButton;\n\n    @FXML\n    private HBox sparkLinePane;\n\n    @FXML\n    private ScrollBar horizontalScrollBar;\n\n    @FXML\n    private GridPane gridPane;\n\n    @FXML\n    private Spinner<Integer> yearSpinner;\n\n    @FXML\n    private ScrollBar verticalScrollBar;\n\n    @FXML\n    private TreeTableView<Account> accountTreeView;\n\n    @FXML\n    private TableView<Account> periodTable;\n\n    @FXML\n    private TableView<Account> accountSummaryTable;\n\n    @FXML\n    private TableView<AccountGroup> periodSummaryTable;\n\n    @FXML\n    private TableView<AccountGroup> accountGroupPeriodSummaryTable;\n\n    @FXML\n    private TableView<AccountGroup> accountTypeTable;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final SimpleObjectProperty<Budget> budget = new SimpleObjectProperty<>();\n\n    private BudgetResultsModel budgetResultsModel;\n\n    /**\n     * This list is updated to track the expanded rows of the TreeTableView.\n     * This should be the model for all account specific tables\n     */\n    private final ObservableList<Account> expandedAccountList = FXCollections.observableArrayList();\n\n    /**\n     * This list is updated to track the displayed AccountGroups.\n     * This should be the model for all account specific tables\n     */\n    private final ObservableList<AccountGroup> accountGroupList = FXCollections.observableArrayList();\n\n    private final DoubleProperty rowHeight = new SimpleDoubleProperty();\n\n    /**\n     * Bind the max and minimum values of every column to this width.\n     */\n    private final DoubleProperty columnWidth = new SimpleDoubleProperty(INITIAL_WIDTH);\n\n    /**\n     * Bind the max and minimum values of every column to this width.\n     */\n    private final DoubleProperty remainingColumnWidth = new SimpleDoubleProperty(INITIAL_WIDTH);\n\n    /**\n     * The is the minimum column width required to display the largest numeric value. Value is cached and only\n     * updated with budget or transaction changes\n     */\n    private double minColumnWidth = INITIAL_WIDTH;\n\n    /**\n     * The is the minimum column width required to display the largest numeric summary value.\n     */\n    private final DoubleProperty minSummaryColumnWidth = new SimpleDoubleProperty(INITIAL_WIDTH);\n\n    /**\n     * Current index to be used for scrolling the display.  0 is the first period is displayed to the left\n     */\n    private int index;\n\n    /**\n     * The number of visible columns.\n     */\n    private final IntegerProperty visibleColumnCount = new SimpleIntegerProperty(1);\n\n    /**\n     * The number of periods in the model.\n     */\n    private final IntegerProperty periodCount = new SimpleIntegerProperty(1);\n\n    private final ReadWriteLock lock = new ReentrantReadWriteLock();\n\n    /**\n     * Listens to changes to the width of the period table and optimizes the column widths.\n     */\n    private ChangeListener<Number> tableWidthChangeListener;\n\n    /**\n     * Listens for changes to the font scale\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Number> fontScaleListener;\n\n    /**\n     * Rate limiting executor.\n     */\n    private ScheduledThreadPoolExecutor rateLimitExecutor;\n\n    /**\n     * Maximum update period in milliseconds\n     */\n    private static final int UPDATE_PERIOD = 350;\n\n    /**\n     * Used to alter timing for rate limiting the first boot for a better visual effect\n     */\n    private volatile boolean booted = false;\n\n    private double startDragX;\n\n    private double startDragWidth;\n\n    /**\n     * Pseudo divider width is a function of the base text height\n     */\n    private double dividerWidth;\n\n    @FXML\n    private void initialize() {\n        runningTotalsButton.selectedProperty().setValue(preferences.getBoolean(RUNNING_TOTALS, false));\n\n        rateLimitExecutor = new ScheduledThreadPoolExecutor(1,\n                new DefaultDaemonThreadFactory(\"Budget View Rate Limit Executor\"),\n                new ThreadPoolExecutor.DiscardPolicy());\n\n        tableWidthChangeListener = (observable, oldValue, newValue) -> {\n            if (newValue != null && !oldValue.equals(newValue)) {\n                optimizeColumnWidths();\n            }\n        };\n\n        handleFontHeightChange();\n\n        yearSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(\n                LocalDate.now().getYear() - YEAR_MARGIN, LocalDate.now().getYear() + YEAR_MARGIN,\n                LocalDate.now().getYear(), 1));\n\n        accountTreeView.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS, StyleClass.HIDE_VERTICAL_CSS);\n        accountTreeView.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);\n        accountTreeView.setShowRoot(false);\n        accountTreeView.setEditable(true);\n        accountTreeView.fixedCellSizeProperty().bind(rowHeight);\n\n        accountSummaryTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        accountSummaryTable.getStyleClass().addAll(StyleClass.HIDDEN_ROW_FOCUS);\n        accountSummaryTable.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS, StyleClass.HIDE_VERTICAL_CSS);\n        accountSummaryTable.setItems(expandedAccountList);\n        accountSummaryTable.fixedCellSizeProperty().bind(rowHeight);\n        accountSummaryTable.setSelectionModel(new NullTableViewSelectionModel<>(accountSummaryTable));\n\n        accountTypeTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        accountTypeTable.getStyleClass().addAll(StyleClass.HIDDEN_COLUMN_HEADER, StyleClass.HIDDEN_ROW_FOCUS);\n        accountTypeTable.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS, StyleClass.HIDE_VERTICAL_CSS);\n        accountTypeTable.setItems(accountGroupList);\n        accountTypeTable.fixedCellSizeProperty().bind(rowHeight);\n        accountTypeTable.prefHeightProperty()\n                .bind(rowHeight.multiply(Bindings.size(accountGroupList)).add(BORDER_MARGIN));\n        accountTypeTable.setSelectionModel(new NullTableViewSelectionModel<>(accountTypeTable));\n\n        // widths need to be bound to the tree view widths for drag/resizing to work\n        accountTypeTable.minWidthProperty().bind(accountTreeView.minWidthProperty());\n        accountTypeTable.prefWidthProperty().bind(accountTreeView.prefWidthProperty());\n\n        accountGroupPeriodSummaryTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        accountGroupPeriodSummaryTable.getStyleClass().addAll(StyleClass.HIDDEN_COLUMN_HEADER, StyleClass.HIDDEN_ROW_FOCUS);\n        accountGroupPeriodSummaryTable.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS, StyleClass.HIDE_VERTICAL_CSS);\n        accountGroupPeriodSummaryTable.setItems(accountGroupList);\n        accountGroupPeriodSummaryTable.fixedCellSizeProperty().bind(rowHeight);\n        accountGroupPeriodSummaryTable.prefHeightProperty()\n                .bind(rowHeight.multiply(Bindings.size(accountGroupList)).add(BORDER_MARGIN));\n        accountGroupPeriodSummaryTable.setSelectionModel(new NullTableViewSelectionModel<>(accountGroupPeriodSummaryTable));\n\n        buildAccountTreeTable();\n        buildAccountTypeTable();\n        buildAccountSummaryTable();\n        buildAccountGroupSummaryTable();\n\n        accountSummaryTable.maxWidthProperty().bind(minSummaryColumnWidth.multiply(3.0).add(BORDER_MARGIN));\n        accountGroupPeriodSummaryTable.maxWidthProperty().bind(minSummaryColumnWidth.multiply(3.0).add(BORDER_MARGIN));\n\n        accountSummaryTable.minWidthProperty().bind(minSummaryColumnWidth.multiply(3.0).add(BORDER_MARGIN));\n        accountGroupPeriodSummaryTable.minWidthProperty().bind(minSummaryColumnWidth.multiply(3.0).add(BORDER_MARGIN));\n\n        accountTreeView.expandedItemCountProperty().addListener((observable, oldValue, newValue)\n                -> JavaFXUtils.runLater(this::updateExpandedAccountList));\n\n        // Calling handleBudgetChange which works for most changes, but can trigger an exception.\n        // handleBudgetUpdate rate limits and prevents an exception.\n        final ChangeListener<Object> budgetChangeListener = (observable, oldValue, newValue) -> handleBudgetUpdate();\n\n        budget.addListener(budgetChangeListener);\n        yearSpinner.valueProperty().addListener(budgetChangeListener);\n        runningTotalsButton.selectedProperty().addListener(budgetChangeListener);\n        visibleColumnCount.addListener(budgetChangeListener);\n\n        runningTotalsButton.selectedProperty().addListener((observable, oldValue, newValue) ->\n                preferences.putBoolean(RUNNING_TOTALS, newValue));\n\n        /* Setting the tables as un-managed effectively removes these tables from the GridPane.  The tables are\n           redundant if showing the amounts as running balances. */\n        accountSummaryTable.managedProperty().bind(runningTotalsButton.selectedProperty().not());\n        accountGroupPeriodSummaryTable.managedProperty().bind(runningTotalsButton.selectedProperty().not());\n\n        horizontalScrollBar.setMin(0);\n        horizontalScrollBar.maxProperty().bind(periodCount.subtract(visibleColumnCount));\n        horizontalScrollBar.setUnitIncrement(1);\n        horizontalScrollBar.disableProperty().bind(periodCount.lessThanOrEqualTo(1));\n\n        // shift the table right and left with the ScrollBar value\n        horizontalScrollBar.valueProperty().addListener((observable, oldValue, newValue) -> {\n\n                    /* must be synchronized to prevent a race condition from multiple events and an out of\n                     * bounds exception */\n                    synchronized (this) {\n\n                        /* don't try unless columns exist.  This can occur if the UI is not large enough to display\n                         * a minimum of one period of information.\n                         */\n                        if (periodTable.getColumns().size() > 0) {\n                            final int newIndex = (int) Math.round(newValue.doubleValue());\n\n                            if (newIndex > index) {\n                                while (newIndex > index) {\n                                    handleShiftRight();\n                                }\n                            } else if (newIndex < index) {\n                                while (newIndex < index) {\n                                    handleShiftLeft();\n                                }\n                            }\n                        }\n                    }\n                }\n        );\n\n        // listen for changes in the font scale and update.  Listener needs to be weak to prevent memory leaks\n        fontScaleListener = (observable, oldValue, newValue) -> handleFontHeightChange();\n        ThemeManager.fontScaleProperty().addListener(new WeakChangeListener<>(fontScaleListener));\n\n        accountTreeView.setOnMouseMoved(this::handleMouseMove);         // cursor handler\n        accountTreeView.setOnMouseDragged(this::handleDividerDrag);     // drag handler\n        accountTreeView.setOnMousePressed(this::handleMouseClicked);    // drag handler\n\n        accountTypeTable.setOnMouseMoved(this::handleMouseMove);         // cursor handler\n        accountTypeTable.setOnMouseDragged(this::handleDividerDrag);     // drag handler\n        accountTypeTable.setOnMousePressed(this::handleMouseClicked);    // drag handler\n\n        JavaFXUtils.runLater(() -> accountTreeView.setPrefWidth(preferences.getDouble(ACCOUNT_COLUMN_WIDTH, INITIAL_WIDTH * 2)));\n    }\n\n    /**\n     * Determines if the cursor is hovering over the pseudo divider\n     *\n     * @param xPos x position of the scene\n     * @return true if hovering over the divider\n     */\n    private boolean isOnDivider(final double xPos) {\n        final Point2D nodeInScene\n                = accountTreeView.localToScene(accountTreeView.getLayoutX(), accountTreeView.getLayoutY());\n\n        return Math.abs(accountTreeView.getWidth() + nodeInScene.getX() - xPos) < (dividerWidth * 0.5);\n    }\n\n    private void handleDividerDrag(final MouseEvent event) {\n        accountTreeView.setPrefWidth(Math.max(startDragWidth + event.getSceneX() - startDragX, INITIAL_WIDTH * 2));\n        preferences.putDouble(ACCOUNT_COLUMN_WIDTH, accountTreeView.getWidth());\n        event.consume();\n    }\n\n    /**\n     * Saves information for calculating drag/resize of the account tree column\n     *\n     * @param event Mouse event\n     */\n    private void handleMouseClicked(final MouseEvent event) {\n        startDragX = event.getSceneX();\n        startDragWidth = accountTreeView.getWidth();\n        event.consume();\n    }\n\n    /**\n     * Handles changing the cursor shape when hovering over the pseudo divider\n     *\n     * @param event Mouse event\n     */\n    private void handleMouseMove(final MouseEvent event) {\n        gridPane.getScene().setCursor(event != null && isOnDivider(event.getSceneX()) ? Cursor.H_RESIZE : Cursor.DEFAULT);\n    }\n\n    private void rateLimitUpdate(final Runnable runnable) {\n        rateLimitExecutor.schedule(() -> {\n            if (rateLimitExecutor.getQueue().size() < 1) {   // ignore if we already have one waiting in the queue\n                JavaFXUtils.runNow(runnable);                // update at the front of the queue with rate limiting\n            }\n\n            booted = true;\n        }, booted ? UPDATE_PERIOD : 0, TimeUnit.MILLISECONDS);\n    }\n\n    @FXML\n    private void handleShiftLeft() {\n        lock.writeLock().lock();\n\n        try {\n            // remove the right column\n            periodTable.getColumns().remove(visibleColumnCount.get() - 1);\n            periodSummaryTable.getColumns().remove(visibleColumnCount.get() - 1);\n\n            index--;\n\n            // insert a new column to the left\n            periodTable.getColumns().add(0, buildAccountPeriodResultsColumn(index));\n            periodSummaryTable.getColumns().add(0, buildAccountPeriodSummaryColumn(index));\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    @FXML\n    private void handleShiftRight() {\n        lock.writeLock().lock();\n\n        try {\n            // remove leftmost column\n            periodTable.getColumns().remove(0);\n            periodSummaryTable.getColumns().remove(0);\n\n            int newColumn = index + visibleColumnCount.get();\n\n            newColumn = Math.min(newColumn, budgetResultsModel.getDescriptorList().size() - 1);\n\n            // add a new column to the right\n            periodTable.getColumns().add(buildAccountPeriodResultsColumn(newColumn));\n            periodSummaryTable.getColumns().add(buildAccountPeriodSummaryColumn(newColumn));\n\n            index++;\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    BudgetResultsModel getBudgetResultsModel() {\n        return budgetResultsModel;\n    }\n\n    SimpleObjectProperty<Budget> budgetProperty() {\n        return budget;\n    }\n\n    private void handleFontHeightChange() {\n        rowHeight.set(ThemeManager.getBaseTextHeight() * ROW_HEIGHT_MULTIPLIER);\n        dividerWidth = ThemeManager.getBaseTextHeight();    // update divider width\n    }\n\n    /**\n     * The table view will lazily create the ScrollBars which makes finding them tricky.  We need to check for\n     * their existence and try again later if they do not exist.\n     * <p>\n     * Synchronize binding, otherwise the ScrollBars get a bit confused and do not respond to a scroll wheel\n     */\n    private synchronized void bindScrollBars() {\n        final Optional<ScrollBar> accountScrollBar = JavaFXUtils.findVerticalScrollBar(accountTreeView);\n        final Optional<ScrollBar> vDataScrollBar = JavaFXUtils.findVerticalScrollBar(periodTable);\n        final Optional<ScrollBar> accountSumScrollBar = JavaFXUtils.findVerticalScrollBar(accountSummaryTable);\n\n        if (vDataScrollBar.isEmpty() || accountScrollBar.isEmpty() || accountSumScrollBar.isEmpty()) {\n            // re-spawn off the off the application thread\n            new Thread(this::bindScrollBars).start();\n        } else {    // all here, lets bind then now\n            JavaFXUtils.runLater(() -> {\n                verticalScrollBar.minProperty().bindBidirectional(accountScrollBar.get().minProperty());\n                verticalScrollBar.maxProperty().bindBidirectional(accountScrollBar.get().maxProperty());\n                verticalScrollBar.valueProperty().bindBidirectional(accountScrollBar.get().valueProperty());\n\n                accountScrollBar.get().valueProperty().bindBidirectional(vDataScrollBar.get().valueProperty());\n                accountSumScrollBar.get().valueProperty().bindBidirectional(vDataScrollBar.get().valueProperty());\n            });\n        }\n    }\n\n    /**\n     * Model must be rebuilt if the year or a budget property is changed.\n     * <p>\n     * This method is synchronized to limit more than one update attempt at a time.\n     */\n    private synchronized void handleBudgetChange() {\n        lock.writeLock().lock();\n\n        try {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (budget.get() != null && engine != null) {\n\n                // unregister listener from the old model because the model will be replaced\n                if (budgetResultsModel != null) {\n                    budgetResultsModel.removeMessageListener(this); // unregister from the old model\n                }\n\n                // Build the new results model\n                budgetResultsModel = new BudgetResultsModel(budget.get(), yearSpinner.getValue(),\n                        engine.getDefaultCurrency(), runningTotalsButton.isSelected());\n\n                // model has changed, calculate the minimum column width for the summary columns\n                minSummaryColumnWidth.set(calculateMinSummaryWidthColumnWidth());\n\n                // model has changed, calculate the minimum column width the data model needs\n                minColumnWidth = calculateMinPeriodColumnWidth();\n\n                // register the listener with the new model\n                budgetResultsModel.addMessageListener(this);    // register with the new model\n\n                // update the number of periods the budget model has\n                periodCount.set(budgetResultsModel.getDescriptorList().size());\n\n                // load the model\n                loadModel();\n            } else {\n                JavaFXUtils.runLater(() -> {\n                    accountTreeView.setRoot(null);\n                    expandedAccountList.clear();\n                    accountGroupList.clear();\n                });\n            }\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * Maintains the list of expanded accounts.\n     */\n    private synchronized void updateExpandedAccountList() {\n        final int count = accountTreeView.getExpandedItemCount();\n\n        // Create a new list and update the observable list in one shot to minimize visual updates\n        final List<Account> accountList = new ArrayList<>();\n        for (int i = 0; i < count; i++) {\n            accountList.add(accountTreeView.getTreeItem(i).getValue());\n        }\n\n        expandedAccountList.setAll(accountList);\n    }\n\n    private void loadModel() {\n        lock.readLock().lock();\n\n        try {\n            loadAccountTree();\n\n            accountGroupList.setAll(budgetResultsModel.getAccountGroupList());\n\n            optimizeColumnWidths();\n\n            buildPeriodTable();\n            buildPeriodSummaryTable();\n\n            updateExpandedAccountList();\n\n            updateSparkLines();\n\n            JavaFXUtils.runLater(this::bindScrollBars);\n\n            JavaFXUtils.runLater(this::focusCurrentPeriod);\n        } finally {\n            lock.readLock().unlock();\n        }\n    }\n\n    private void optimizeColumnWidths() {\n        lock.writeLock().lock();\n\n        try {\n            final double availWidth = periodTable.getWidth() - BORDER_MARGIN;   // width of the table\n\n            /* calculate the number of visible columns, period columns are 3 columns wide\n               the maximum is caped to no more than the number of available periods */\n            final int maxVisible = Math.min((int) Math.floor(availWidth / (minColumnWidth * 3.0)),\n                    budgetResultsModel.getDescriptorList().size());\n\n            /* update the number of visible columns factoring in the size of the descriptor list */\n            visibleColumnCount.set((Math.min(budgetResultsModel.getDescriptorList().size(), maxVisible)));\n\n            if (visibleColumnCount.get() == 0) {\n                periodTable.placeholderProperty()\n                        .setValue(new Label(resources.getString(\"Message.Warn.WindowWidth\")));\n\n                periodSummaryTable.placeholderProperty()\n                        .setValue(new Label(resources.getString(\"Message.Warn.WindowWidth\")));\n            }\n\n            final double width = Math.floor(availWidth /\n                    Math.min(budgetResultsModel.getDescriptorList().size() * 3, maxVisible * 3));\n\n            columnWidth.set(width);\n\n            double remainder = availWidth - (maxVisible * 3.0 * width);\n\n            remainingColumnWidth.set(Math.floor(width + (remainder / 3.0)));\n        } finally {\n            lock.writeLock().unlock();\n        }\n    }\n\n    void focusCurrentPeriod() {\n        final LocalDate now = LocalDate.now();\n\n        final List<BudgetPeriodDescriptor> budgetPeriodDescriptorList = budgetResultsModel.getDescriptorList();\n\n        for (int i = 0; i < budgetPeriodDescriptorList.size(); i++) {\n            final BudgetPeriodDescriptor budgetPeriodDescriptor = budgetPeriodDescriptorList.get(i);\n\n            if (budgetPeriodDescriptor.isBetween(now)) {\n                final int index = Math.max(Math.min(i, periodCount.subtract(visibleColumnCount).intValue()), 0);\n\n                JavaFXUtils.runLater(() -> horizontalScrollBar.setValue(index));\n                break;\n            }\n        }\n    }\n\n    private void loadAccountTree() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final TreeItem<Account> root = new TreeItem<>(engine.getRootAccount());\n        root.setExpanded(true);\n\n        accountTreeView.setRoot(root);\n        loadChildren(root);\n    }\n\n    private synchronized void loadChildren(final TreeItem<Account> parentItem) {\n        final Account parent = parentItem.getValue();\n\n        parent.getChildren(Comparators.getAccountByCode()).stream().filter(budgetResultsModel::includeAccount)\n                .forEach(child -> {\n                    final TreeItem<Account> childItem = new TreeItem<>(child);\n                    childItem.setExpanded(true);\n                    parentItem.getChildren().add(childItem);\n\n                    if (child.getChildCount() > 0) {\n                        loadChildren(childItem);\n                    }\n                });\n    }\n\n    /**\n     * Constructs the tree table.\n     *\n     * @see Stage#showAndWait() for need to push {@code handleEditAccountGoals(account)} to the platform thread\n     */\n    private void buildAccountTreeTable() {\n        // empty column header is needed to match other table columns\n        final TreeTableColumn<Account, String> headerColumn = new TreeTableColumn<>(\"\");\n\n        final TreeTableColumn<Account, Account> nameColumn\n                = new TreeTableColumn<>(resources.getString(\"Column.Account\"));\n\n        nameColumn.setCellFactory(param -> new AccountTreeTableCell());\n        nameColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getValue()));\n\n        nameColumn.setOnEditStart(event -> {\n            final Account account = event.getRowValue().getValue();\n\n            if (account != null && !account.isPlaceHolder()) {\n\n                // push to the edit of the platform thread to avoid an IllegalStateException\n                // \"showAndWait is not allowed during animation or layout processing\" exception\n                // this may be reached outside the platform thread if a uses is click happy\n                if (Platform.isFxApplicationThread()) {\n                    handleEditAccountGoals(account);\n                } else {\n                    JavaFXUtils.runLater(() -> handleEditAccountGoals(account));\n                }\n            }\n        });\n        nameColumn.setEditable(true);\n\n        headerColumn.getColumns().add(nameColumn);\n\n        accountTreeView.getColumns().add(headerColumn);\n    }\n\n    private void buildAccountTypeTable() {\n        final TableColumn<AccountGroup, String> nameColumn = new TableColumn<>(resources.getString(\"Column.Type\"));\n        nameColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().toString()));\n\n        accountTypeTable.getColumns().add(nameColumn);\n    }\n\n    private void buildAccountSummaryTable() {\n        final TableColumn<Account, BigDecimal> headerColumn = new TableColumn<>(resources.getString(\"Title.Summary\"));\n\n        final TableColumn<Account, BigDecimal> budgetedColumn\n                = new TableColumn<>(resources.getString(\"Column.Budgeted\"));\n\n        budgetedColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(param.getValue()).getBudgeted());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        budgetedColumn.setCellFactory(param -> new AccountCommodityFormatTableCell());\n        lockColumnBehavior(budgetedColumn, minSummaryColumnWidth);\n\n        headerColumn.getColumns().add(budgetedColumn);\n\n        final TableColumn<Account, BigDecimal> actualColumn = new TableColumn<>(resources.getString(\"Column.Actual\"));\n        actualColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(param.getValue()).getChange());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        actualColumn.setCellFactory(param -> new AccountCommodityFormatTableCell());\n        lockColumnBehavior(actualColumn, minSummaryColumnWidth);\n\n        headerColumn.getColumns().add(actualColumn);\n\n        final TableColumn<Account, BigDecimal> remainingColumn\n                = new TableColumn<>(resources.getString(\"Column.Remaining\"));\n\n        remainingColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(param.getValue()).getRemaining());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        remainingColumn.setCellFactory(param -> new AccountCommodityFormatTableCell());\n        lockColumnBehavior(remainingColumn, minSummaryColumnWidth);\n\n        headerColumn.getColumns().add(remainingColumn);\n        headerColumn.resizableProperty().set(false);\n\n        accountSummaryTable.getColumns().add(headerColumn);\n    }\n\n    private TableColumn<Account, BigDecimal> buildAccountPeriodResultsColumn(final int index) {\n\n        final BudgetPeriodDescriptor descriptor = budgetResultsModel.getDescriptorList().get(index);\n\n        // determine if the column is to be highlighted if the period is not yearly\n        final Boolean highlight = (descriptor.isBetween(LocalDate.now()) ? Boolean.TRUE : Boolean.FALSE)\n                && budget.get().getBudgetPeriod() != Period.YEARLY;\n\n        final TableColumn<Account, BigDecimal> headerColumn = new TableColumn<>(descriptor.getPeriodDescription());\n\n        final TableColumn<Account, BigDecimal> budgetedColumn\n                = new TableColumn<>(resources.getString(\"Column.Budgeted\"));\n\n        budgetedColumn.getProperties().put(NOW, highlight);\n        budgetedColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(descriptor,\n                        param.getValue()).getBudgeted());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        budgetedColumn.setCellFactory(param -> new AccountCommodityFormatTableCell());\n        lockColumnBehavior(budgetedColumn, columnWidth);\n\n        headerColumn.getColumns().add(budgetedColumn);\n\n        final TableColumn<Account, BigDecimal> actualColumn = new TableColumn<>(resources.getString(\"Column.Actual\"));\n\n        actualColumn.getProperties().put(NOW, highlight);\n        actualColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(descriptor,\n                        param.getValue()).getChange());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        actualColumn.setCellFactory(param -> new AccountCommodityFormatTableCell());\n        lockColumnBehavior(actualColumn, columnWidth);\n\n        headerColumn.getColumns().add(actualColumn);\n\n        final TableColumn<Account, BigDecimal> remainingColumn\n                = new TableColumn<>(resources.getString(\"Column.Remaining\"));\n\n        remainingColumn.getProperties().put(NOW, highlight);\n        remainingColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(descriptor,\n                        param.getValue()).getRemaining());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        remainingColumn.setCellFactory(param -> new AccountCommodityFormatTableCell());\n        lockColumnBehavior(remainingColumn, remainingColumnWidth);\n\n        headerColumn.getColumns().add(remainingColumn);\n\n        headerColumn.resizableProperty().set(false);\n\n        return headerColumn;\n    }\n\n    /**\n     * The period table must be rebuilt because of JavaFx issues.\n     */\n    private void buildPeriodTable() {\n        // remove the old listener so it does not leak\n        periodTable.widthProperty().removeListener(tableWidthChangeListener);\n\n        // recreate the table and load the new one into the grid pane\n        final int row = GridPane.getRowIndex(periodTable);\n        final int column = GridPane.getColumnIndex(periodTable);\n        gridPane.getChildren().remove(periodTable);\n\n        periodTable = new TableView<>();\n        GridPane.setConstraints(periodTable, column, row);\n        gridPane.getChildren().add(periodTable);\n\n        periodTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        periodTable.getStyleClass().addAll(StyleClass.HIDDEN_ROW_FOCUS);\n        periodTable.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS, StyleClass.HIDE_VERTICAL_CSS);\n        periodTable.fixedCellSizeProperty().bind(rowHeight);\n        periodTable.setSelectionModel(new NullTableViewSelectionModel<>(periodTable));\n\n        // index exceeds allowed value because the user reduced the period count, reset to the maximum allowed value\n        if (index > budgetResultsModel.getDescriptorList().size() - visibleColumnCount.get()) {\n            index = budgetResultsModel.getDescriptorList().size() - visibleColumnCount.get();\n        }\n\n        final int periodCount = Math.min(visibleColumnCount.get(), budgetResultsModel.getDescriptorList().size());\n\n        for (int i = index; i < index + periodCount; i++) {\n            periodTable.getColumns().add(buildAccountPeriodResultsColumn(i));\n        }\n\n        periodTable.setItems(expandedAccountList);\n        periodTable.widthProperty().addListener(tableWidthChangeListener);\n\n        periodTable.setOnMouseMoved(this::handleMouseMove);         // cursor\n        periodTable.setOnMouseDragged(this::handleDividerDrag);     // drag\n        periodTable.setOnMousePressed(this::handleMouseClicked);    // drag\n    }\n\n    private TableColumn<AccountGroup, BigDecimal> buildAccountPeriodSummaryColumn(final int index) {\n        final BudgetPeriodDescriptor descriptor = budgetResultsModel.getDescriptorList().get(index);\n\n        // determine if the column is to be highlighted if the period is not yearly\n        final Boolean highlight = (descriptor.isBetween(LocalDate.now()) ? Boolean.TRUE : Boolean.FALSE)\n                && budget.get().getBudgetPeriod() != Period.YEARLY;\n\n        final TableColumn<AccountGroup, BigDecimal> headerColumn = new TableColumn<>(descriptor.getPeriodDescription());\n\n        final TableColumn<AccountGroup, BigDecimal> budgetedColumn\n                = new TableColumn<>(resources.getString(\"Column.Budgeted\"));\n\n        budgetedColumn.getProperties().put(NOW, highlight);\n        budgetedColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(descriptor,\n                        param.getValue()).getBudgeted());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        budgetedColumn.setCellFactory(param -> new AccountGroupTableCell());\n        lockColumnBehavior(budgetedColumn, columnWidth);\n\n        headerColumn.getColumns().add(budgetedColumn);\n\n        final TableColumn<AccountGroup, BigDecimal> actualColumn\n                = new TableColumn<>(resources.getString(\"Column.Actual\"));\n\n        actualColumn.getProperties().put(NOW, highlight);\n        actualColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(descriptor, param.getValue()).getChange());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        actualColumn.setCellFactory(param -> new AccountGroupTableCell());\n        lockColumnBehavior(actualColumn, columnWidth);\n\n        headerColumn.getColumns().add(actualColumn);\n\n        final TableColumn<AccountGroup, BigDecimal> remainingColumn\n                = new TableColumn<>(resources.getString(\"Column.Remaining\"));\n\n        remainingColumn.getProperties().put(NOW, highlight);\n        remainingColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(descriptor,\n                        param.getValue()).getRemaining());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        remainingColumn.setCellFactory(param -> new AccountGroupTableCell());\n\n        // the max width is not bound to allow last column to grow and fill any voids\n        lockColumnBehavior(remainingColumn, remainingColumnWidth);\n\n        headerColumn.getColumns().add(remainingColumn);\n\n        return headerColumn;\n    }\n\n    private void lockColumnBehavior(final TableColumn<?, ?> column, final DoubleProperty columnWidth) {\n        column.minWidthProperty().bind(columnWidth);\n        column.maxWidthProperty().bind(columnWidth);\n        column.setSortable(false);\n        column.resizableProperty().set(false);\n        column.setReorderable(false);\n    }\n\n    /**\n     * The period summary table must be rebuilt because of JavaFx issues.\n     */\n    private void buildPeriodSummaryTable() {\n        // recreate the table and load the new one into the grid pane\n        final int row = GridPane.getRowIndex(periodSummaryTable);\n        final int column = GridPane.getColumnIndex(periodSummaryTable);\n\n        gridPane.getChildren().remove(periodSummaryTable);\n        periodSummaryTable = new TableView<>();\n\n        GridPane.setConstraints(periodSummaryTable, column, row);\n        gridPane.getChildren().add(periodSummaryTable);\n\n        periodSummaryTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        periodSummaryTable.getStyleClass().add(StyleClass.HIDDEN_COLUMN_HEADER);\n        periodSummaryTable.getStyleClass().addAll(StyleClass.HIDDEN_ROW_FOCUS);\n        periodSummaryTable.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS, StyleClass.HIDE_VERTICAL_CSS);\n        periodSummaryTable.fixedCellSizeProperty().bind(rowHeight);\n        periodSummaryTable.prefHeightProperty()\n                .bind(rowHeight.multiply(Bindings.size(accountGroupList)).add(BORDER_MARGIN));\n        periodSummaryTable.setSelectionModel(new NullTableViewSelectionModel<>(periodSummaryTable));\n\n        final int periodCount = Math.min(visibleColumnCount.get(), budgetResultsModel.getDescriptorList().size());\n\n        for (int i = index; i < index + periodCount; i++) {\n            periodSummaryTable.getColumns().add(buildAccountPeriodSummaryColumn(i));\n        }\n\n        periodSummaryTable.setItems(accountGroupList);\n\n        periodSummaryTable.setOnMouseMoved(this::handleMouseMove);         // cursor\n        periodSummaryTable.setOnMouseDragged(this::handleDividerDrag);     // drag\n        periodSummaryTable.setOnMousePressed(this::handleMouseClicked);    // drag\n    }\n\n    private void buildAccountGroupSummaryTable() {\n        final TableColumn<AccountGroup, BigDecimal> headerColumn\n                = new TableColumn<>(resources.getString(\"Title.Summary\"));\n\n        final TableColumn<AccountGroup, BigDecimal> budgetedColumn\n                = new TableColumn<>(resources.getString(\"Column.Budgeted\"));\n\n        budgetedColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(param.getValue()).getBudgeted());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        budgetedColumn.setCellFactory(param -> new AccountGroupTableCell());\n        budgetedColumn.minWidthProperty().bind(minSummaryColumnWidth);\n        budgetedColumn.maxWidthProperty().bind(minSummaryColumnWidth);\n        budgetedColumn.setSortable(false);\n        budgetedColumn.resizableProperty().set(false);\n\n        headerColumn.getColumns().add(budgetedColumn);\n\n        final TableColumn<AccountGroup, BigDecimal> actualColumn\n                = new TableColumn<>(resources.getString(\"Column.Actual\"));\n\n        actualColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(param.getValue()).getChange());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        actualColumn.setCellFactory(param -> new AccountGroupTableCell());\n        actualColumn.minWidthProperty().bind(minSummaryColumnWidth);\n        actualColumn.maxWidthProperty().bind(minSummaryColumnWidth);\n        actualColumn.setSortable(false);\n        actualColumn.resizableProperty().set(false);\n\n        headerColumn.getColumns().add(actualColumn);\n\n        final TableColumn<AccountGroup, BigDecimal> remainingColumn\n                = new TableColumn<>(resources.getString(\"Column.Remaining\"));\n\n        remainingColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null) {\n                return new SimpleObjectProperty<>(budgetResultsModel.getResults(param.getValue()).getRemaining());\n            }\n            return new SimpleObjectProperty<>(BigDecimal.ZERO);\n        });\n        remainingColumn.setCellFactory(param -> new AccountGroupTableCell());\n        remainingColumn.minWidthProperty().bind(minSummaryColumnWidth);\n        remainingColumn.maxWidthProperty().bind(minSummaryColumnWidth);\n        remainingColumn.setSortable(false);\n        remainingColumn.resizableProperty().set(false);\n\n        headerColumn.getColumns().add(remainingColumn);\n\n        accountGroupPeriodSummaryTable.getColumns().add(headerColumn);\n    }\n\n    private double calculateMinColumnWidth(final BudgetPeriodResults budgetPeriodResults) {\n        double max = 0;\n        double min = 0;\n\n        max = Math.max(max, budgetPeriodResults.getBudgeted().doubleValue());\n        max = Math.max(max, budgetPeriodResults.getChange().doubleValue());\n        max = Math.max(max, budgetPeriodResults.getRemaining().doubleValue());\n\n        min = Math.min(min, budgetPeriodResults.getBudgeted().doubleValue());\n        min = Math.min(min, budgetPeriodResults.getChange().doubleValue());\n        min = Math.min(min, budgetPeriodResults.getRemaining().doubleValue());\n\n        return Math.max(JavaFXUtils.getDisplayedTextWidth(\n                NumericFormats.getFullCommodityFormat(budgetResultsModel.getBaseCurrency()).format(max) +\n                        BORDER_MARGIN, null), JavaFXUtils.getDisplayedTextWidth(\n                NumericFormats.getFullCommodityFormat(budgetResultsModel.getBaseCurrency()).format(min) +\n                        BORDER_MARGIN, null));\n    }\n\n    private double calculateMinColumnWidth(final BudgetPeriodDescriptor descriptor, final Account account) {\n        return calculateMinColumnWidth(budgetResultsModel.getResults(descriptor, account));\n    }\n\n    private double calculateMinColumnWidth(final Account account) {\n        return calculateMinColumnWidth(budgetResultsModel.getResults(account));\n    }\n\n    private double calculateMinColumnWidth(final BudgetPeriodDescriptor descriptor) {\n        double max = 0;\n        double min = 0;\n\n        for (final AccountGroup accountGroup : accountGroupList) {\n            BudgetPeriodResults budgetPeriodResults = budgetResultsModel.getResults(descriptor, accountGroup);\n            max = Math.max(max, budgetPeriodResults.getBudgeted().doubleValue());\n            max = Math.max(max, budgetPeriodResults.getChange().doubleValue());\n            max = Math.max(max, budgetPeriodResults.getRemaining().doubleValue());\n\n            min = Math.min(min, budgetPeriodResults.getBudgeted().doubleValue());\n            min = Math.min(min, budgetPeriodResults.getChange().doubleValue());\n            min = Math.min(min, budgetPeriodResults.getRemaining().doubleValue());\n        }\n\n        return Math.max(JavaFXUtils.getDisplayedTextWidth(\n                NumericFormats.getFullCommodityFormat(budgetResultsModel.getBaseCurrency()).format(max) +\n                        BORDER_MARGIN, null),\n                JavaFXUtils.getDisplayedTextWidth(\n                        NumericFormats.getFullCommodityFormat(budgetResultsModel.getBaseCurrency()).format(min) +\n                                BORDER_MARGIN, null));\n    }\n\n    private double calculateMinPeriodColumnWidth() {\n        double max = 0;\n\n        for (final BudgetPeriodDescriptor descriptor : budgetResultsModel.getDescriptorList()) {\n            for (final Account account : expandedAccountList) {\n                max = Math.max(max, calculateMinColumnWidth(descriptor, account));\n            }\n        }\n\n        max = Math.max(max, JavaFXUtils.getDisplayedTextWidth(resources.getString(\"Column.Budgeted\")\n                + BORDER_MARGIN, null));\n        max = Math.max(max, JavaFXUtils.getDisplayedTextWidth(resources.getString(\"Column.Actual\")\n                + BORDER_MARGIN, null));\n        max = Math.max(max, JavaFXUtils.getDisplayedTextWidth(resources.getString(\"Column.Remaining\")\n                + BORDER_MARGIN, null));\n\n        return Math.ceil(max);\n    }\n\n    private double calculateMinSummaryWidthColumnWidth() {\n        double max = 0;\n\n        for (final BudgetPeriodDescriptor descriptor : budgetResultsModel.getDescriptorList()) {\n            max = Math.max(max, calculateMinColumnWidth(descriptor));\n        }\n\n        for (final Account account : expandedAccountList) {\n            max = Math.max(max, calculateMinColumnWidth(account));\n        }\n\n        max = Math.max(max, JavaFXUtils.getDisplayedTextWidth(resources.getString(\"Column.Budgeted\")\n                + BORDER_MARGIN, null));\n        max = Math.max(max, JavaFXUtils.getDisplayedTextWidth(resources.getString(\"Column.Actual\")\n                + BORDER_MARGIN, null));\n        max = Math.max(max, JavaFXUtils.getDisplayedTextWidth(resources.getString(\"Column.Remaining\")\n                + BORDER_MARGIN, null));\n\n        return Math.ceil(max);\n    }\n\n    private void handleBudgetUpdate() {\n        rateLimitUpdate(BudgetTableController.this::handleBudgetChange);\n    }\n\n    private void handleTransactionUpdate() {\n        rateLimitUpdate(() -> {\n            synchronized (this) {\n                periodTable.refresh();\n                periodSummaryTable.refresh();\n            }\n            accountSummaryTable.refresh();\n            accountGroupPeriodSummaryTable.refresh();\n\n\n            optimizeColumnWidths();\n\n            updateSparkLines();\n        });\n    }\n\n    private void updateSparkLines() {\n        sparkLinePane.getChildren().clear();\n\n        for (final AccountGroup group : accountGroupList) {\n            List<BigDecimal> remaining = budgetResultsModel.getDescriptorList().parallelStream().map(descriptor ->\n                    budgetResultsModel.getResults(descriptor, group).getRemaining()).collect(Collectors.toList());\n\n            final HBox hBox = new HBox(new Label(group.toString()), new BudgetSparkLine(remaining));\n            hBox.setAlignment(Pos.CENTER_LEFT);\n\n            sparkLinePane.getChildren().add(hBox);\n        }\n    }\n\n    private void handleEditAccountGoals(@NotNull final Account account) {\n        Objects.requireNonNull(account);\n\n        final FXMLUtils.Pair<BudgetGoalsDialogController> pair =\n                FXMLUtils.load(BudgetGoalsDialogController.class.getResource(\"BudgetGoalsDialog.fxml\"),\n                        resources.getString(\"Title.BudgetGoal\") + \" - \" + account.getName());\n\n        pair.getController().startMonthProperty().set(budget.get().getStartMonth());\n        pair.getController().accountProperty().set(account);\n        pair.getController().workingYearProperty().set(yearSpinner.getValue());\n\n        try {\n            final BudgetGoal oldGoal = (BudgetGoal) budgetProperty().get().getBudgetGoal(account).clone();\n            pair.getController().budgetGoalProperty().set(oldGoal);\n        } catch (final CloneNotSupportedException e) {\n            Logger.getLogger(BudgetTableController.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        StageUtils.addBoundsListener(pair.getStage(), BudgetGoalsDialogController.class);\n\n        pair.getStage().showAndWait();\n\n        final Optional<BudgetGoal> optional = pair.getController().getResult();\n\n        optional.ifPresent(budgetGoal -> {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.updateBudgetGoals(budget.get(), account, budgetGoal);\n        });\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        switch (message.getEvent()) {\n            case FILE_CLOSING:\n                budgetResultsModel.removeMessageListener(this);\n                budgetProperty().set(null);\n                break;\n            case BUDGET_REMOVE:\n                if (budget.get().equals(message.getObject(MessageProperty.BUDGET))) {\n                    budget.set(null);\n                    budgetResultsModel.removeMessageListener(this);\n                }\n                break;\n            case ACCOUNT_ADD:\n            case ACCOUNT_MODIFY:\n            case ACCOUNT_REMOVE:\n            case BUDGET_UPDATE:\n            case BUDGET_GOAL_UPDATE:\n                if (budget.get().equals(message.getObject(MessageProperty.BUDGET))) {\n                    handleBudgetUpdate();\n                }\n                break;\n            case TRANSACTION_ADD:\n            case TRANSACTION_REMOVE:\n                handleTransactionUpdate();\n                break;\n            default:\n                break;\n        }\n    }\n\n    private class AccountCommodityFormatTableCell extends TableCell<Account, BigDecimal> {\n\n        AccountCommodityFormatTableCell() {\n            setStyle(\"-fx-alignment: center-right;\");  // Right align\n        }\n\n        @Override\n        protected void updateItem(final BigDecimal amount, final boolean empty) {\n            super.updateItem(amount, empty);  // required\n\n            setId(NORMAL_CELL_ID);   // reset, cell is reused\n\n            final boolean now = Boolean.TRUE == getTableColumn().getProperties().getOrDefault(NOW, Boolean.FALSE);\n\n            if (!empty && amount != null && getTableRow() != null) {\n                final Account account = expandedAccountList.get(getTableRow().getIndex());\n                final NumberFormat format = NumericFormats.getFullCommodityFormat(account.getCurrencyNode());\n\n                setText(format.format(amount));\n\n                if (account.isPlaceHolder()) {\n                    if (amount.signum() < 0) {\n                        setId(now ? TODAY_BOLD_NEGATIVE_LABEL_ID : BOLD_NEGATIVE_LABEL_ID);\n                    } else {\n                        setId(now ? TODAY_BOLD_LABEL_ID : BOLD_LABEL_ID);\n                    }\n                } else {\n                    if (amount.signum() < 0) {\n                        setId(now ? TODAY_NORMAL_NEGATIVE_CELL_ID : NORMAL_NEGATIVE_CELL_ID);\n                    } else {\n                        setId(now ? TODAY_NORMAL_CELL_ID : NORMAL_CELL_ID);\n                    }\n                }\n            } else {\n                setText(null);\n            }\n        }\n    }\n\n    private class AccountGroupTableCell extends TableCell<AccountGroup, BigDecimal> {\n\n        private final NumberFormat format;\n\n        AccountGroupTableCell() {\n            setStyle(\"-fx-alignment: center-right;\");  // Right align\n            format = NumericFormats.getFullCommodityFormat(budgetResultsModel.getBaseCurrency());\n        }\n\n        @Override\n        protected void updateItem(final BigDecimal amount, final boolean empty) {\n            super.updateItem(amount, empty);  // required\n\n            setId(NORMAL_CELL_ID);   // reset, cell is reused\n\n            final boolean now = Boolean.TRUE == getTableColumn().getProperties().getOrDefault(NOW, Boolean.FALSE);\n\n            if (!empty && amount != null && getTableRow() != null) {\n                setText(format.format(amount));\n\n                if (amount.signum() < 0) {\n                    setId(now ? TODAY_NORMAL_NEGATIVE_CELL_ID : NORMAL_NEGATIVE_CELL_ID);\n                } else {\n                    setId(now ? TODAY_NORMAL_CELL_ID : NORMAL_CELL_ID);\n                }\n            } else {\n                setText(null);\n            }\n        }\n    }\n\n    private static class AccountTreeTableCell extends TreeTableCell<Account, Account> {\n\n        @Override\n        protected void updateItem(final Account account, final boolean empty) {\n            super.updateItem(account, empty);  // required\n\n            setId(NORMAL_CELL_ID);   // reset, cell is reused\n\n            if (!empty && account != null && getTreeTableRow() != null) {\n                setText(account.getName());\n\n                if (account.isPlaceHolder()) {\n                    setId(BOLD_LABEL_ID);\n                } else {\n                    setId(NORMAL_CELL_ID);\n                }\n            } else {\n                setText(null);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/BudgetViewController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.io.File;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.UUID;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.layout.BorderPane;\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.report.poi.BudgetResultsExport;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * Primary controller for the Budget view.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetViewController implements MessageListener {\n\n    private static final String EXPORT_DIR = \"exportDir\";\n\n    private static final String LAST_BUDGET = \"lastBudget\";\n\n    @FXML\n    private BorderPane borderPane;\n\n    @FXML\n    private Button exportButton;\n\n    @FXML\n    private Button propertiesButton;\n\n    @FXML\n    private ComboBox<Budget> availableBudgetsComboBox;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private BudgetTableController budgetTableController;\n\n    private final Preferences preferences = Preferences.userNodeForPackage(BudgetViewController.class);\n\n    @FXML\n    private void initialize() {\n        exportButton.disableProperty().bind(availableBudgetsComboBox.valueProperty().isNull());\n        propertiesButton.disableProperty().bind(availableBudgetsComboBox.valueProperty().isNull());\n\n        // push to end of application thread to avoid a race\n        JavaFXUtils.runLater(() -> {\n            budgetTableController\n                    = FXMLUtils.loadFXML(o -> borderPane.setCenter(o), \"BudgetTable.fxml\", resources);\n\n            availableBudgetsComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n                if (newValue != null) {\n                    preferences.put(LAST_BUDGET, newValue.getUuid().toString());\n                }\n\n                JavaFXUtils.runLater(() -> budgetTableController.budgetProperty().set(newValue));\n            });\n\n            loadComboBox();\n\n            MessageBus.getInstance().registerListener(BudgetViewController.this, MessageChannel.BUDGET,\n                    MessageChannel.SYSTEM);\n        });\n    }\n\n    private void loadComboBox() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        // Create a sorted List of active budgets\n        final List<Budget> budgetList = engine.getBudgetList();\n        Collections.sort(budgetList);\n\n        availableBudgetsComboBox.getItems().setAll(budgetList);\n\n        String uuid = preferences.get(LAST_BUDGET, \"\");\n\n        if (!uuid.isEmpty()) {\n            final Budget lastBudget = engine.getBudgetByUuid(UUID.fromString(uuid));\n\n            if (budgetList.contains(lastBudget)) {\n                availableBudgetsComboBox.setValue(lastBudget);\n            } else if (availableBudgetsComboBox.getItems().size() > 0) {\n                availableBudgetsComboBox.setValue(availableBudgetsComboBox.getItems().get(0));\n            }\n        }\n    }\n\n    @FXML\n    private void handleExportAction() {\n        Objects.requireNonNull(budgetTableController);\n\n        final Preferences pref = Preferences.userNodeForPackage(BudgetViewController.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(EXPORT_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.SpreadsheetFiles\") + \" (*.xls, *.xlsx)\",\n                        \"*.xls\", \"*.xlsx\")\n        );\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            pref.put(EXPORT_DIR, file.getParentFile().getAbsolutePath());\n\n            final Task<String> exportTask = new Task<>() {\n                @Override\n                protected String call() {\n                    updateMessage(resources.getString(\"Message.PleaseWait\"));\n                    updateProgress(-1, Long.MAX_VALUE);\n\n                    return BudgetResultsExport.exportBudgetResultsModel(file.toPath(),\n                            budgetTableController.getBudgetResultsModel());\n                }\n            };\n\n            new Thread(exportTask).start();\n\n            StaticUIMethods.displayTaskProgress(exportTask);\n        }\n    }\n\n    @FXML\n    private void handleManagerAction() {\n        BudgetManagerDialogController.showBudgetManager();\n    }\n\n    @FXML\n    private void handlePropertiesAction() {\n        final FXMLUtils.Pair<BudgetPropertiesDialogController> pair =\n                FXMLUtils.load(BudgetPropertiesDialogController.class.getResource(\"BudgetPropertiesDialog.fxml\"),\n                        resources.getString(\"Title.BudgetProperties\"));\n\n        pair.getController().setBudget(availableBudgetsComboBox.getValue());\n\n        pair.getStage().show();\n        pair.getStage().setResizable(false);\n    }\n\n    @FXML\n    private void handleTodayAction() {\n        JavaFXUtils.runLater(budgetTableController::focusCurrentPeriod);\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        switch (message.getEvent()) {\n            case FILE_CLOSING:\n                MessageBus.getInstance().unregisterListener(this, MessageChannel.BUDGET, MessageChannel.SYSTEM);\n                JavaFXUtils.runLater(() -> availableBudgetsComboBox.getItems().clear());\n                break;\n            case BUDGET_REMOVE:\n            case BUDGET_ADD:\n            case BUDGET_UPDATE:\n                JavaFXUtils.runLater(BudgetViewController.this::loadComboBox);\n                break;\n            default:\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/budget/HistoricalBudgetDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.budget;\n\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.TextField;\nimport javafx.scene.text.Text;\nimport javafx.scene.text.TextFlow;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.budget.BudgetFactory;\nimport jgnash.time.Period;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.resource.util.TextResource;\n\n/**\n * A mini wizard for creating a new budget based on historical data.\n *\n * @author Craig Cavanaugh\n */\npublic class HistoricalBudgetDialogController {\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private TextFlow textFlow;\n\n    @FXML\n    private TextField nameTextField;\n\n    @FXML\n    private ComboBox<Period> periodComboBox;\n\n    @FXML\n    private CheckBox roundupCheckBox;\n\n    @FXML\n    private Button okButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        periodComboBox.getItems().addAll(Period.values());\n        periodComboBox.setValue(Period.MONTHLY);\n\n        textFlow.getChildren().addAll(new Text(TextResource.getString(\"NewBudgetOne.txt\")));\n\n        okButton.disableProperty().bind(nameTextField.textProperty().isEmpty());\n    }\n\n    @FXML\n    private void handleOkAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Budget budget = BudgetFactory.buildAverageBudget(periodComboBox.getValue(), nameTextField.getText(),\n                roundupCheckBox.isSelected());\n\n        new Thread(() -> {\n            if (!engine.addBudget(budget)) {\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.NewBudget\"));\n            }\n        }).start();\n\n        ((Stage) okButton.getScene().getWindow()).close();\n    }\n\n    @FXML\n    private void handleCancelAction() {\n        ((Stage) okButton.getScene().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/main/ConsoleDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.main;\n\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.Charset;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.logging.Logger;\n\nimport javafx.animation.Animation;\nimport javafx.animation.KeyFrame;\nimport javafx.animation.Timeline;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.event.ActionEvent;\nimport javafx.event.EventHandler;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ProgressBar;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.input.Clipboard;\nimport javafx.scene.input.ClipboardContent;\nimport javafx.scene.text.Font;\nimport javafx.scene.text.Text;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\nimport javafx.util.Duration;\n\nimport jgnash.engine.Engine;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Controller for the console dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class ConsoleDialogController {\n\n    private static final long BYTES_PER_MB = 1024000;\n\n    private  static final int REFRESH_PERIOD = 500;\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ProgressBar memoryUsageProgressBar;\n\n    @FXML\n    private Text memoryUsageText;\n\n    @FXML\n    private TextArea consoleArea;\n\n    private PrintStream oldOutStream;\n\n    private PrintStream oldErrStream;\n\n    private PrintStream outStream;\n\n    private PrintStream errStream;\n\n    private final LogHandler logHandler = new LogHandler();\n\n    private Timeline timeline;\n\n    private final Runtime runtime = Runtime.getRuntime();\n\n    private static final AtomicBoolean visible = new AtomicBoolean(false);\n\n    @FXML\n    void initialize() {\n        oldErrStream = System.err;\n        oldOutStream = System.out;\n\n        // Force a monospaced font\n        consoleArea.setFont(Font.font(\"Monospaced\", consoleArea.getFont().getSize()));\n\n        Engine.getLogger().addHandler(logHandler);\n\n        try {\n            outStream = new PrintStream(new OutputStream() {\n                @Override\n                public void write(int b) {\n                    oldOutStream.write(b);\n                    JavaFXUtils.runLater(() -> consoleArea.appendText(String.valueOf((char) b)));\n                }\n            }, false, Charset.defaultCharset().name());\n        } catch (final UnsupportedEncodingException ex) {\n            Logger.getLogger(ConsoleDialogController.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        try {\n            errStream = new PrintStream(new OutputStream() {\n                @Override\n                public void write(int b) {\n                    oldErrStream.write(b);\n                    JavaFXUtils.runLater(() -> consoleArea.appendText(String.valueOf((char) b)));\n                }\n            }, false, Charset.defaultCharset().name());\n        } catch (final UnsupportedEncodingException ex) {\n            Logger.getLogger(ConsoleDialogController.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        // Plug in the new streams\n        System.setOut(outStream);\n        System.setErr(errStream);\n\n        timeline = new Timeline(new KeyFrame(Duration.millis(REFRESH_PERIOD), new EventHandler<>() {\n            private long total;\n\n            private long used;\n\n            private long oldUsed;\n\n            private static final int diff = 1;\n\n            @Override\n            public void handle(ActionEvent event) {\n                total = runtime.totalMemory() / BYTES_PER_MB;\n                used = total - runtime.freeMemory() / BYTES_PER_MB;\n                if (used < oldUsed - diff || used > oldUsed + diff) {\n                    JavaFXUtils.runLater(() -> {\n                        memoryUsageProgressBar.setProgress((double) used / (double) total);\n                        memoryUsageText.setText(used + \"/\" + total + \" MB\");\n                    });\n                    oldUsed = used;\n                }\n            }\n        }));\n\n        timeline.setCycleCount(Animation.INDEFINITE);\n        timeline.play();\n\n        // Close with the main application\n        MainView.getPrimaryStage()\n                .addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, event -> handleCloseAction());\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        System.setErr(oldErrStream);\n        System.setOut(oldOutStream);\n\n        Engine.getLogger().removeHandler(logHandler);\n\n        timeline.stop();\n\n        ((Stage) parent.get().getWindow()).close();\n        visible.set(false);\n    }\n\n    @FXML\n    private void handleForceGarbageCollection() {\n        System.gc();\n    }\n\n    @FXML\n    private void handleCopyToClipboard() {\n        final ClipboardContent content = new ClipboardContent();\n        content.putString(consoleArea.getText());\n\n        Clipboard.getSystemClipboard().setContent(content);\n    }\n\n    public static void show() {\n        if (!visible.get()) {\n            visible.set(true);\n\n            final FXMLUtils.Pair<ConsoleDialogController> pair =\n                    FXMLUtils.load(ConsoleDialogController.class.getResource(\"ConsoleDialog.fxml\"),\n                            ResourceUtils.getString(\"Title.Console\"));\n\n            // Override the defaults set by FXMLUtils\n            pair.getStage().initModality(Modality.NONE);\n            pair.getStage().initOwner(null);\n\n            pair.getStage().show();\n        }\n    }\n\n    private class LogHandler extends Handler {\n        @Override\n        public void publish(LogRecord record) {\n            JavaFXUtils.runLater(() -> consoleArea.appendText(record.getMessage() + System.lineSeparator()));\n        }\n\n        @Override\n        public void flush() {\n        }\n\n        @Override\n        public void close() throws SecurityException {\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/main/MainToolBarController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.main;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.tasks.CloseFileTask;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Primary ToolBar Controller.\n *\n * @author Craig Cavanaugh\n */\npublic class MainToolBarController implements MessageListener {\n\n    final private BooleanProperty disabled = new SimpleBooleanProperty(true);\n\n    @FXML\n    private Button closeButton;\n\n    @FXML\n    private Button updateCurrencies;\n\n    @FXML\n    private Button updateSecurities;\n\n    @FXML\n    private void initialize() {\n\n        closeButton.disableProperty().bind(disabled);\n        updateCurrencies.disableProperty().bind(disabled);\n        updateSecurities.disableProperty().bind(disabled);\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM);\n    }\n\n    @FXML\n    private void handleOpenAction() {\n        StaticUIMethods.showOpenDialog();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {\n            CloseFileTask.initiateFileClose();\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        switch (event.getEvent()) {\n            case FILE_LOAD_SUCCESS:\n                JavaFXUtils.runLater(() -> disabled.set(false));\n                break;\n            case FILE_CLOSING:\n            case FILE_LOAD_FAILED:\n                JavaFXUtils.runLater(() -> disabled.set(true));\n                break;\n            default:\n                break;\n        }\n    }\n\n    @FXML\n    private void handleSecuritiesUpdateAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        if (engine != null) {\n            engine.startSecuritiesUpdate(0);\n        }\n    }\n\n    @FXML\n    private void handleCurrenciesUpdateAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        if (engine != null) {\n            engine.startExchangeRateUpdate(0);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/main/MainView.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.main;\n\nimport java.io.File;\nimport java.text.MessageFormat;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.concurrent.Task;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.MenuBar;\nimport javafx.scene.control.ToolBar;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.layout.VBox;\nimport javafx.scene.paint.Color;\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.net.security.UpdateFactory;\nimport jgnash.plugin.FxPlugin;\nimport jgnash.plugin.Plugin;\nimport jgnash.plugin.PluginFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.Version;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.BusyPane;\nimport jgnash.uifx.control.StatusBar;\nimport jgnash.uifx.control.TabViewPane;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.tasks.BootEngineTask;\nimport jgnash.uifx.tasks.CloseFileTask;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.accounts.AccountsViewController;\nimport jgnash.uifx.views.budget.BudgetViewController;\nimport jgnash.uifx.views.recurring.RecurringViewController;\nimport jgnash.uifx.views.register.RegisterViewController;\nimport jgnash.util.DefaultDaemonThreadFactory;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * JavaFX version of jGnash.\n *\n * @author Craig Cavanaugh\n */\npublic class MainView implements MessageListener {\n\n    private static final String TITLE;\n\n    private static final String LAST_TAB = \"lastTab\";\n\n    private static final Logger logger = Logger.getLogger(MainView.class.getName());\n\n    private final ResourceBundle resources = ResourceUtils.getBundle();\n\n    private final Executor backgroundExecutor =\n            Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory(\"Main View Background Executor\"));\n\n    private final StatusBarLogHandler statusBarLogHandler = new StatusBarLogHandler();\n\n    private Stage primaryStage;\n\n    private StatusBar statusBar;\n\n    private TabViewPane tabViewPane;\n\n    private MenuBar menuBar;\n\n    private BusyPane busyPane;\n\n    private final Preferences preferences = Preferences.userNodeForPackage(MainView.class);\n\n    // Maintain a reference for removal to prevent memory leaks\n    private ChangeListener<Number> tabListener;\n\n    /**\n     * Application Singleton.\n     */\n    private static MainView instance;\n\n    static {\n        TITLE = Version.getAppName() + \" - \" + Version.getAppVersion();\n    }\n\n    public MainView() {\n        if (instance == null) {\n            instance = this;\n        }\n    }\n\n    public static MainView getInstance() {\n        return instance;\n    }\n\n    @NotNull\n    public static Logger getLogger() {\n        return logger;\n    }\n\n    /**\n     * Allows lookup for a scene node.  Intended for plugin use.\n     *\n     * @param selector node id to look for\n     *\n     * @return the first first node with the matching id.  Returns null if not found\n     * @see Node#lookup(String)\n     */\n    public Node lookup(@NotNull String selector) {\n        return primaryStage.getScene().lookup(selector);\n    }\n\n    /**\n     * Provides access to the application MenuBar for plugins.\n     *\n     * @return Application {@code MenuBar}\n     */\n    public MenuBar getMenuBar() {\n        return menuBar;\n    }\n\n    public void start(final Stage stage, @Nullable final File dataFile, final char[] password,\n                      @Nullable final String host, final int port) throws Exception {\n        ThemeManager.restoreLastUsedTheme();\n\n        primaryStage = stage;\n\n        busyPane = new BusyPane();\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(MenuBarController.class.getResource(\"MainMenuBar.fxml\"), resources);\n            menuBar = fxmlLoader.load();\n        } catch (final Exception e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n\n        final ToolBar mainToolBar = FXMLLoader.load(\n                Objects.requireNonNull(MainToolBarController.class.getResource(\"MainToolBar.fxml\")), resources);\n\n        tabViewPane = new TabViewPane();\n\n        final VBox top = new VBox();\n        top.getChildren().addAll(menuBar, mainToolBar);\n        top.setFillWidth(true);\n\n        statusBar = new StatusBar();\n\n        final BorderPane borderPane = new BorderPane();\n        borderPane.setTop(top);\n        borderPane.setCenter(tabViewPane);\n        borderPane.setBottom(statusBar);\n\n        final StackPane stackPane = new StackPane();\n        stackPane.getChildren().addAll(borderPane, busyPane);\n\n        final Scene scene = new Scene(stackPane, 640, 480);\n        ThemeManager.applyStyleSheets(scene);\n\n        scene.getRoot().styleProperty().bind(ThemeManager.styleProperty());\n\n        stage.setTitle(TITLE);\n        stage.getIcons().add(StaticUIMethods.getApplicationIcon());\n        stage.setScene(scene);\n        stage.setResizable(true);\n\n        // enforce a min width to prevent it from disappearing with a bad click and drag\n        stage.setMinWidth(640);\n        stage.setMinHeight(480);\n\n        installHandlers();\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM);\n\n        StageUtils.addBoundsListener(stage, MainView.class);\n\n        stage.show();\n\n        Engine.addLogHandler(statusBarLogHandler);\n        EngineFactory.addLogHandler(statusBarLogHandler);\n        UpdateFactory.addLogHandler(statusBarLogHandler);\n        logger.addHandler(statusBarLogHandler); // listen to my own logger\n\n        stage.toFront();\n        stage.requestFocus();\n\n        if (host != null) { // connect to a remote server instead of loading a local file\n            new Thread(() -> {\n                try {\n                    Thread.sleep(BootEngineTask.FORCED_DELAY);\n                    backgroundExecutor.execute(() -> JavaFXUtils.runLater(()\n                            -> BootEngineTask.initiateBoot(null, password, true, host, port)));\n                } catch (InterruptedException e) {\n                    logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                    Thread.currentThread().interrupt();\n                }\n            }).start();\n        } else if (dataFile != null) { // Load the specified file, this overrides the last open file\n            new Thread(() -> {\n                try {\n                    Thread.sleep(BootEngineTask.FORCED_DELAY);\n                    backgroundExecutor.execute(() -> JavaFXUtils.runLater(()\n                            -> BootEngineTask.initiateBoot(dataFile.getAbsolutePath(), password, false, null, 0)));\n                } catch (InterruptedException e) {\n                    logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                    Thread.currentThread().interrupt();\n                }\n            }).start();\n        } else if (Options.openLastProperty().get()) { // Load the last open file if enabled\n            new Thread(() -> {\n                try {\n                    Thread.sleep(BootEngineTask.FORCED_DELAY);\n                    backgroundExecutor.execute(() -> JavaFXUtils.runLater(BootEngineTask::openLast));\n                } catch (InterruptedException e) {\n                    logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                    Thread.currentThread().interrupt();\n                }\n            }).start();\n        }\n\n        loadPlugins();\n\n        checkForLatestRelease();\n    }\n\n    private static void checkForLatestRelease() {\n        new Thread(() -> {\n            try {\n                Thread.sleep(BootEngineTask.FORCED_DELAY * 3L);\n                if (Options.checkForUpdatesProperty().get()) {\n                    if (!Version.isReleaseCurrent()) {\n                        JavaFXUtils.runLater(() ->\n                                StaticUIMethods.displayMessage(ResourceUtils.getString(\"Message.NewVersion\")));\n                    }\n                    logger.info(\"Version check performed\");\n                }\n            } catch (InterruptedException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                Thread.currentThread().interrupt();\n            }\n        }).start();\n    }\n\n    private void addViews() {\n        backgroundExecutor.execute(() -> JavaFXUtils.runLater(() -> tabViewPane.addTab(\n                FXMLUtils.load(AccountsViewController.class.getResource(\"AccountsView.fxml\"), resources),\n                resources.getString(\"Tab.Accounts\"))));\n\n        backgroundExecutor.execute(() -> JavaFXUtils.runLater(() -> tabViewPane.addTab(\n                FXMLUtils.load(RegisterViewController.class.getResource(\"RegisterView.fxml\"), resources),\n                resources.getString(\"Tab.Register\"))));\n\n        backgroundExecutor.execute(() -> JavaFXUtils.runLater(() -> tabViewPane.addTab(\n                FXMLUtils.load(RecurringViewController.class.getResource(\"RecurringView.fxml\"), resources),\n                resources.getString(\"Tab.Reminders\"))));\n\n        backgroundExecutor.execute(() -> JavaFXUtils.runLater(() -> tabViewPane.addTab(\n                FXMLUtils.load(BudgetViewController.class.getResource(\"BudgetView.fxml\"), resources),\n                resources.getString(\"Tab.Budgeting\"))));\n\n        backgroundExecutor.execute(() ->\n                JavaFXUtils.runLater(() -> {\n                    tabViewPane.getSelectionModel().select(preferences.getInt(LAST_TAB, 0));\n\n                    tabListener = (observable, oldValue, newValue) -> {\n                        if (newValue != null && newValue.intValue() > -1) { // -1 will occur when all tabs are removed\n                            preferences.putInt(LAST_TAB, newValue.intValue());\n                        }\n                    };\n\n                    tabViewPane.getSelectionModel()\n                            .selectedIndexProperty().addListener(new WeakChangeListener<>(tabListener));\n                }));\n    }\n\n    private void removeViews() {\n        // Push to the background executor so that a load before current load is finished won't trigger a NPE\n        backgroundExecutor.execute(() -> JavaFXUtils.runLater(() -> {\n            tabViewPane.getTabs().clear();\n            tabListener = null;\n        }));\n    }\n\n    private static void installHandlers() {\n\n        // Close the file cleanly if it is still open\n        getPrimaryStage().addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, windowEvent -> {\n            PluginFactory.stopPlugins();    // Stop plugins\n\n            if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {\n                windowEvent.consume();  // consume the event and let the shutdown handler deal with closure\n                CloseFileTask.initiateShutdown();\n            }\n        });\n    }\n\n    /**\n     * Provides access to the primary {@code Application} stage.\n     *\n     * @return the primary stage\n     */\n    public static Stage getPrimaryStage() {\n        return getInstance().primaryStage;\n    }\n\n    /**\n     * Requests focus for the primary {@code Stage}.\n     *\n     * @see javafx.stage.Stage#requestFocus()\n     */\n    public static void requestFocus() {\n        getPrimaryStage().requestFocus();\n    }\n\n    /**\n     * Sets the application in a busy state and waits for the provided {@code Task} to complete.\n     * The caller is responsible for starting the task.\n     *\n     * @param task long running {@code Task} to monitor\n     */\n    public void setBusy(@Nullable final Task<?> task) {\n        busyPane.setTask(task);\n    }\n\n    private static void loadPlugins() {\n\n        // Wrap in an exception handler so a poorly behaving plugin does not prevent application startup\n        try {\n            PluginFactory.loadPlugins(plugin -> plugin instanceof FxPlugin);    // Load only the Fx based plugins\n            PluginFactory.startPlugins(Plugin.PluginPlatform.Fx);\n        } catch (final Exception e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        switch (event.getEvent()) {\n            case FILE_LOAD_SUCCESS:\n                JavaFXUtils.runLater(() -> {\n                    updateTitle();\n                    addViews();\n                });\n                break;\n            case FILE_CLOSING:\n                JavaFXUtils.runLater(() -> {\n                    removeViews();\n                    updateTitle();\n                });\n                break;\n            case FILE_IO_ERROR:\n                logger.warning(resources.getString(\"Message.Error.IOError\"));\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.IOError\"));\n                break;\n            case FILE_LOAD_FAILED:\n                logger.warning(resources.getString(\"Message.Error.LoadingFile\"));\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.LoadingFile\"));\n                break;\n            case FILE_NOT_FOUND:\n                logger.warning(resources.getString(\"Message.Error.FileNotFound\"));\n                break;\n            case ACCOUNT_REMOVE_FAILED:\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.AccountRemove\"));\n                break;\n            case BACKGROUND_PROCESS_STARTED:\n                setBusyBackground(true);\n                break;\n            case BACKGROUND_PROCESS_STOPPED:\n                setBusyBackground(false);\n                break;\n            default:\n                break;\n        }\n\n    }\n\n    private void setBusyBackground(final boolean busy) {\n        JavaFXUtils.runLater(() -> {\n            if (busy) {\n                statusBar.progressProperty().set(-1);\n            } else {\n                statusBar.progressProperty().set(0);\n            }\n        });\n    }\n\n    private void updateTitle() {\n        backgroundExecutor.execute(() -> {\n            Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            JavaFXUtils.runLater(() -> {\n                if (engine != null) {\n                    getPrimaryStage().setTitle(TITLE + \"  [\" + EngineFactory.getActiveDatabase() + ']');\n                } else {\n                    getPrimaryStage().setTitle(TITLE);\n                }\n            });\n        });\n    }   \n\n    private class StatusBarLogHandler extends Handler {\n\n        private static final double GRAPHIC_SIZE = 11;\n\n        final Node info;\n\n        final Node warning;\n\n        final Node severe;\n\n        StatusBarLogHandler() {\n            info = new MaterialDesignLabel(MaterialDesignLabel.MDIcon.INFO, GRAPHIC_SIZE);\n            warning = new MaterialDesignLabel(MaterialDesignLabel.MDIcon.EXCLAMATION_TRIANGLE, GRAPHIC_SIZE);\n            severe = new MaterialDesignLabel(MaterialDesignLabel.MDIcon.BUG, GRAPHIC_SIZE, Color.DARKRED);\n        }\n\n        @Override\n        public void close() {\n        \t// not used\n        }\n\n        @Override\n        public void flush() {\n        \t// not used\n        }\n\n        @Override\n        public synchronized void publish(final LogRecord record) {\n            if (record.getLevel() == Level.INFO) {\n                updateStatus(MessageFormat.format(record.getMessage(), record.getParameters()), info);\n            } else if (record.getLevel() == Level.WARNING) {\n                updateStatus(MessageFormat.format(record.getMessage(), record.getParameters()), warning);\n            } else if (record.getLevel() == Level.SEVERE) {\n                updateStatus(MessageFormat.format(record.getMessage(), record.getParameters()), severe);\n            }\n        }\n\n        private void updateStatus(final String status, final Node glyph) {\n            JavaFXUtils.runLater(() -> {\n                statusBar.textProperty().set(status);\n                statusBar.graphicProperty().set(glyph);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/main/MenuBarController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.main;\n\nimport java.io.File;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.application.Platform;\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.collections.ListChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuBar;\nimport javafx.scene.control.MenuItem;\nimport javafx.stage.FileChooser;\n\nimport jgnash.convert.importat.ImportFilter;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.plugin.PluginFactory;\nimport jgnash.report.pdf.FontRegistry;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.about.AboutDialogController;\nimport jgnash.uifx.actions.DefaultCurrencyAction;\nimport jgnash.uifx.actions.DefaultLocaleAction;\nimport jgnash.uifx.actions.ExecuteJavaScriptAction;\nimport jgnash.uifx.actions.ExportAccountsAction;\nimport jgnash.uifx.actions.ImportAccountsAction;\nimport jgnash.uifx.actions.ImportOfxAction;\nimport jgnash.uifx.actions.ImportQifAction;\nimport jgnash.uifx.dialog.ChangeDatabasePasswordDialogController;\nimport jgnash.uifx.dialog.ImportScriptsDialogController;\nimport jgnash.uifx.dialog.PackDatabaseDialogController;\nimport jgnash.uifx.dialog.RemoteConnectionDialogController;\nimport jgnash.uifx.dialog.TagManagerDialogController;\nimport jgnash.uifx.dialog.currency.AddRemoveCurrencyController;\nimport jgnash.uifx.dialog.currency.EditExchangeRatesController;\nimport jgnash.uifx.dialog.currency.ModifyCurrencyController;\nimport jgnash.uifx.dialog.options.OptionDialogController;\nimport jgnash.uifx.dialog.options.TransactionNumberDialogController;\nimport jgnash.uifx.dialog.security.CreateModifySecuritiesController;\nimport jgnash.uifx.dialog.security.SecurityHistoryController;\nimport jgnash.uifx.report.ReportActions;\nimport jgnash.uifx.skin.BaseColorDialogController;\nimport jgnash.uifx.skin.FontSizeDialogController;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.tasks.CloseFileTask;\nimport jgnash.uifx.tasks.SaveAsTask;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.budget.BudgetManagerDialogController;\nimport jgnash.uifx.views.recurring.RecurringDialogController;\nimport jgnash.uifx.views.register.RegisterStage;\nimport jgnash.uifx.wizard.file.NewFileWizard;\nimport jgnash.util.FileUtils;\n\n/**\n * Primary Menu Controller.\n *\n * @author Craig Cavanaugh\n */\npublic class MenuBarController implements MessageListener {\n\n    private final BooleanProperty disabled = new SimpleBooleanProperty(true);\n    \n    private final BooleanProperty investDisabled = new SimpleBooleanProperty(false);\n\n    @FXML\n    private MenuItem tagManagerMenuItem;\n\n    @FXML\n    private MenuItem configureTranImportFiltersMenuItem;\n\n    @FXML\n    private MenuItem packDatabaseMenuItem;\n\n    @FXML\n    private MenuItem recurringTransactionsMenuItem;\n\n    @FXML\n    private MenuItem budgetManagerMenuItem;\n\n    @FXML\n    private MenuItem shutdownServerMenuItem;\n\n    @FXML\n    private MenuItem changePasswordMenuItem;\n\n    @FXML\n    private MenuItem saveAsMenuItem;\n\n    @FXML\n    private MenuItem importAccountsMenuItem;\n\n    @FXML\n    private MenuItem exportAccountsMenuItem;\n\n    @FXML\n    private Menu reportMenu;\n\n    @FXML\n    private MenuItem importQifMenuItem;\n\n    @FXML\n    private MenuItem importOfxMenuItem;\n\n    @FXML\n    private MenuItem transNumberListMenuItem;\n\n    @FXML\n    private MenuItem optionsMenuItem;\n\n    @FXML\n    private MenuItem portfolioReportMenuItem;\n\n    @FXML\n    private Menu themesMenu;\n\n    @FXML\n    private Menu currenciesMenu;\n\n    @FXML\n    private Menu securitiesMenu;\n\n    @FXML\n    private Menu windowMenu;\n\n    @FXML\n    private MenuBar menuBar;\n\n    @FXML\n    private MenuItem openMenuItem;\n\n    @FXML\n    private MenuItem closeMenuItem;\n\n    @FXML\n    private MenuItem exitMenuItem;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        budgetManagerMenuItem.disableProperty().bind(disabled);\n        changePasswordMenuItem.disableProperty().bind(disabled.not());\n        closeMenuItem.disableProperty().bind(disabled);\n        configureTranImportFiltersMenuItem.disableProperty().bind(disabled);\n        currenciesMenu.disableProperty().bind(disabled);\n        exportAccountsMenuItem.disableProperty().bind(disabled);\n        importAccountsMenuItem.disableProperty().bind(disabled);\n        importOfxMenuItem.disableProperty().bind(disabled);\n        importQifMenuItem.disableProperty().bind(disabled);\n        recurringTransactionsMenuItem.disableProperty().bind(disabled);\n        reportMenu.disableProperty().bind(disabled);\n        saveAsMenuItem.disableProperty().bind(disabled);\n        securitiesMenu.disableProperty().bind(disabled);\n        shutdownServerMenuItem.disableProperty().bind(disabled.not());\n        tagManagerMenuItem.disableProperty().bind(disabled);\n        transNumberListMenuItem.disableProperty().bind(disabled);\n        packDatabaseMenuItem.disableProperty().bind(disabled.not());\n        portfolioReportMenuItem.disableProperty().bind(Bindings.or(disabled, investDisabled));\n\n        windowMenu.disableProperty().bind(Bindings.or(disabled, RegisterStage.registerStageList().emptyProperty()));\n\n        RegisterStage.registerStageList().addListener((ListChangeListener<RegisterStage>) c -> {\n            while (c.next()) {\n                if (c.wasAdded()) {\n                    c.getAddedSubList().forEach(MenuBarController.this::addWindowMenuItem);\n                } else if (c.wasRemoved()) {\n                    c.getAddedSubList().forEach(MenuBarController.this::removeWindowMenuItem);\n                }\n            }\n        });\n\n        ThemeManager.addKnownThemes(themesMenu);\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM);\n        MessageBus.getInstance().registerListener(this, MessageChannel.ACCOUNT);\n    }\n\n    private void addWindowMenuItem(final RegisterStage registerStage) {\n        final MenuItem menuItem = new MenuItem(registerStage.accountProperty().get().getName());\n        menuItem.setUserData(registerStage);\n\n        menuItem.setOnAction(event -> {\n            final RegisterStage stage = (RegisterStage) menuItem.getUserData();\n            stage.requestFocus();\n        });\n\n        registerStage.setOnHiding(event -> windowMenu.getItems().removeAll(menuItem));\n\n        windowMenu.getItems().add(0, menuItem);\n    }\n\n    private void removeWindowMenuItem(final RegisterStage registerStage) {\n        windowMenu.getItems().stream().filter(item -> item.getUserData() == registerStage).\n                forEach(item -> windowMenu.getItems().remove(item));\n    }\n\n    @FXML\n    private void handleExitAction() {\n        PluginFactory.stopPlugins();    // Stop plugins\n\n        if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {\n            CloseFileTask.initiateShutdown();\n        } else {\n            Platform.exit();\n        }\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        if (EngineFactory.getEngine(EngineFactory.DEFAULT) != null) {\n            CloseFileTask.initiateFileClose();\n        }\n    }\n\n    @FXML\n    private void handleOpenAction() {\n        StaticUIMethods.showOpenDialog();\n    }\n\n    @FXML\n    private void updateSecurities() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        Objects.requireNonNull(engine);\n\n        engine.startSecuritiesUpdate(0);\n    }\n\n    @FXML\n    private void updateCurrencies() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        Objects.requireNonNull(engine);\n\n        engine.startExchangeRateUpdate(0);\n    }\n\n    @FXML\n    private void handleNewAction() {\n        NewFileWizard.showAndWait();\n    }\n\n    @FXML\n    private void handleAboutAction() {\n        AboutDialogController.showAndWait();\n    }\n\n    @FXML\n    private void changeDefaultLocale() {\n        DefaultLocaleAction.showAndWait();\n    }\n\n    @FXML\n    private void closeAllWindows() {\n        // create a copy to avoid concurrent modification issues\n        final RegisterStage[] stages = RegisterStage.registerStageList()\n                .toArray(new RegisterStage[0]);\n\n        for (final RegisterStage stage : stages) {\n            stage.close();\n        }\n    }\n\n    @FXML\n    private void handleCreateModifySecuritiesAction() {\n        final FXMLUtils.Pair<CreateModifySecuritiesController> pair =\n                FXMLUtils.load(CreateModifySecuritiesController.class.getResource(\"CreateModifySecurities.fxml\"),\n                        resources.getString(\"Title.CreateModifyCommodities\"));\n\n        StageUtils.addBoundsListener(pair.getStage(), CreateModifySecuritiesController.class);\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleSecuritiesHistoryAction() {\n        final FXMLUtils.Pair<SecurityHistoryController> pair =\n                FXMLUtils.load(SecurityHistoryController.class.getResource(\"SecurityHistory.fxml\"),\n                        resources.getString(\"Title.ModifySecHistory\"));\n\n        StageUtils.addBoundsListener(pair.getStage(), SecurityHistoryController.class);\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleSecurityHistoryImportAction() {\n        final FXMLUtils.Pair<SecurityHistoryController> pair =\n                FXMLUtils.load(SecurityHistoryController.class.getResource(\"HistoricalImport.fxml\"),\n                        resources.getString(\"Title.HistoryImport\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleAddRemoveCurrenciesAction() {\n        final FXMLUtils.Pair<AddRemoveCurrencyController> pair =\n                FXMLUtils.load(AddRemoveCurrencyController.class.getResource(\"AddRemoveCurrency.fxml\"),\n                        resources.getString(\"Title.AddRemCurr\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleSetDefaultCurrencyAction() {\n        DefaultCurrencyAction.showAndWait();\n    }\n\n    @FXML\n    private void handleChangeDatabasePasswordAction() {\n        final FXMLUtils.Pair<ChangeDatabasePasswordDialogController> pair =\n                FXMLUtils.load(ChangeDatabasePasswordDialogController.class.getResource(\"ChangePasswordDialog.fxml\"),\n                        resources.getString(\"Title.ChangePassword\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleModifyCurrenciesAction() {\n        final FXMLUtils.Pair<ModifyCurrencyController> pair = FXMLUtils.load(ModifyCurrencyController.class.getResource(\"ModifyCurrency.fxml\"),\n                resources.getString(\"Title.ModifyCurrencies\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleEditExchangeRatesAction() {\n        EditExchangeRatesController.show();\n    }\n\n    @FXML\n    private void handleFontSizeAction() {\n        final FXMLUtils.Pair<FontSizeDialogController> pair = FXMLUtils.load(FontSizeDialogController.class.getResource(\"FontSizeDialog.fxml\"),\n                resources.getString(\"Title.FontSize\"));\n\n        pair.getStage().setResizable(false);\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleBaseColorAction() {\n        final FXMLUtils.Pair<BaseColorDialogController> pair =\n                FXMLUtils.load(BaseColorDialogController.class.getResource(\"BaseColorDialog.fxml\"),\n                        resources.getString(\"Title.BaseColor\"));\n\n        pair.getStage().setResizable(false);\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleApplyStyleAction() {\n        FileChooser fileChooser = new FileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.Open\"));\n\n        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resources.getString(\"Label.CssFiles\"), \"*.css\"));\n\n        final File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null && FileUtils.getFileExtension(file.toString()).equalsIgnoreCase(\"css\")) {\n            if (ThemeManager.setUserStyle(file.toPath())) {\n                JavaFXUtils.runLater(() -> StaticUIMethods.displayMessage(resources.getString(\"Message.Info.RestartToApply\")));\n            }\n        }\n    }\n\n    @FXML\n    private void handleClearStyleAction() {\n        if (ThemeManager.setUserStyle(null)) {\n            JavaFXUtils.runLater(() -> StaticUIMethods.displayMessage(resources.getString(\"Message.Info.RestartToApply\")));\n        }\n    }\n\n    @FXML\n    private void handleShowOptionDialog() {\n\n        // tickle the font registry to load the dialog faster\n        new Thread(FontRegistry::getFontList).start();\n\n        final FXMLUtils.Pair<OptionDialogController> pair = FXMLUtils.load(OptionDialogController.class.getResource(\"OptionDialog.fxml\"),\n                resources.getString(\"Title.Options\"));\n\n        StageUtils.addBoundsListener(pair.getStage(), OptionDialogController.class);\n\n        pair.getStage().setResizable(false);\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleShowTranNumberListDialog() {\n        TransactionNumberDialogController.showAndWait();\n    }\n\n    @FXML\n    private void handleShowConsoleDialog() {\n        ConsoleDialogController.show();\n    }\n\n    @FXML\n    private void handleExecuteJavaScriptFile() {\n        ExecuteJavaScriptAction.showAndWait();\n    }\n\n    @FXML\n    private void handleImportOFXAction() {\n        ImportOfxAction.showAndWait();\n    }\n\n    @FXML\n    private void handleImportQIFAction() {\n        ImportQifAction.showAndWait();\n    }\n\n    @FXML\n    private void handleIncomeExpensePieChart() {\n        ReportActions.displayIncomeExpensePieChart();\n    }\n\n    @FXML\n    private void handleIncomeExpensePayeePieChart() {\n        ReportActions.displayIncomeExpensePayeePieChart();\n    }\n\n    @FXML\n    private void handleIncomeExpenseBarChart() {\n        ReportActions.displayIncomeExpenseBarChart();\n    }\n\n    @FXML\n    private void handleExportProfitLoss() {\n        ReportActions.exportProfitLossReport();\n    }\n\n    @FXML\n    private void handleExportBalanceByMonthCSVReport() {\n        ReportActions.exportBalanceByMonthCSVReport();\n    }\n\n    @FXML\n    private void handleDisplayPortfolioReport() {\n        ReportActions.displayPortfolioReport();\n    }\n\n    @FXML\n    private void handleDisplayTransactionTagPieChart() {\n        ReportActions.displayTransactionTagPieChart();\n    }\n\n    @FXML\n    private void handleDisplayAccountBalanceChart() {\n        ReportActions.displayAccountBalanceChart();\n    }\n\n    @FXML\n    private void handleDisplayListOfAccountsReport() {\n        ReportActions.displayListOfAccountsReport();\n    }\n\n    @FXML\n    private void handleDisplayAccountRegisterReport() {\n        ReportActions.displayAccountRegisterReport(null);\n    }\n\n    @FXML\n    private void handleDisplayProfitLossReport() {\n        ReportActions.displayProfitLossReport();\n    }\n\n    @FXML\n    private void handleDisplayBalanceSheetReport() {\n        ReportActions.displayBalanceSheetReport();\n    }\n\n    @FXML\n    private void handleDisplayNetWorthReport() {\n        ReportActions.displayNetWorthReport();\n    }\n\n    @FXML\n    private void handleImportAccountsAction() {\n        ImportAccountsAction.showAndWait();\n    }\n\n    @FXML\n    private void handleExportAccountsAction() {\n        ExportAccountsAction.showAndWait();\n    }\n\n    @FXML\n    private void handleSaveAsAction() {\n        SaveAsTask.start();\n    }\n\n    @FXML\n    private void handlePackDatabaseAction() {\n        final FXMLUtils.Pair<PackDatabaseDialogController> pair =\n                FXMLUtils.load(PackDatabaseDialogController.class.getResource(\"PackDatabaseDialog.fxml\"),\n                        resources.getString(\"Title.PackDatabase\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleShutDownServerAction() {\n        final FXMLUtils.Pair<RemoteConnectionDialogController> pair =\n                FXMLUtils.load(RemoteConnectionDialogController.class.getResource(\"RemoteConnectionDialog.fxml\"),\n                        resources.getString(\"Title.ConnectServer\"));\n\n        final RemoteConnectionDialogController controller = pair.getController();\n\n        pair.getStage().showAndWait();\n\n        if (controller.getResult()) {\n            // Message buss is on port + 1\n            JavaFXUtils.runLater(() -> MessageBus.getInstance().shutDownRemoteServer(controller.getHost(),\n                    controller.getPort() + 1, controller.getPassword()));\n        }\n    }\n\n    @FXML\n    private void handleBudgetManagerAction() {\n        BudgetManagerDialogController.showBudgetManager();\n    }\n\n    @FXML\n    private void handleTagManagerAction() {\n        TagManagerDialogController.showTagManager();\n    }\n\n    @FXML\n    private void handleShowRecurringTransactionsAction() {\n        final FXMLUtils.Pair<RecurringDialogController> pair =\n                FXMLUtils.load(RecurringDialogController.class.getResource(\"RecurringDialog.fxml\"),\n                        resources.getString(\"Title.Reminders\"));\n\n        pair.getStage().show();\n    }\n\n    @FXML\n    private void handleShowTranImportFilterDialog() {\n        final FXMLUtils.Pair<ImportScriptsDialogController> pair =\n                FXMLUtils.load(ImportScriptsDialogController.class.getResource(\"ImportScriptsDialog.fxml\"),\n                        resources.getString(\"Title.ConfigTransImportFilters\"));\n\n        pair.getController().setEnabledScripts(ImportFilter.getEnabledImportFilters());\n\n        pair.getController().setAcceptanceConsumer(ImportFilter::saveEnabledImportFilters);\n\n        pair.getStage().show();\n\n        StageUtils.addBoundsListener(pair.getStage(), ImportScriptsDialogController.class, MainView.getPrimaryStage());\n    }\n\n    private void checkAccountTypes() {\n        Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine != null) {\n            investDisabled.setValue(engine.getInvestmentAccountList().isEmpty());\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        switch (event.getEvent()) {\n            case FILE_LOAD_SUCCESS:\n                JavaFXUtils.runLater(() -> {\n                    disabled.set(false);\n                    checkAccountTypes();\n                });\n                break;\n            case FILE_CLOSING:\n                JavaFXUtils.runLater(() -> {\n                    closeAllWindows();\n                    disabled.set(true);\n                });\n                break;\n            case ACCOUNT_ADD:\n            case ACCOUNT_REMOVE:\n                JavaFXUtils.runLater(this::checkAccountTypes);\n                break;\n            default:\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/main/OpenDatabaseController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.main;\n\nimport java.io.File;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.PasswordField;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.EngineFactory;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.actions.DatabasePathAction;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.tasks.BootEngineTask;\n\n/**\n * FXML Controller for opening a file or database.\n *\n * @author Craig Cavanaugh\n */\npublic class OpenDatabaseController {\n\n    private static final String LAST_DIR = \"LastDir\";\n\n    @FXML\n    private Button selectFileButton;\n\n    @FXML\n    protected ButtonBar buttonBar;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    protected TextField localDatabaseField;\n\n    @FXML\n    protected CheckBox remoteServerCheckBox;\n\n    @FXML\n    protected TextField databaseServerField;\n\n    @FXML\n    protected IntegerTextField portField;\n\n    @FXML\n    protected PasswordField passwordField;\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        setDatabaseField(EngineFactory.getLastDatabase());\n        databaseServerField.setText(EngineFactory.getLastHost());\n        portField.setInteger(EngineFactory.getLastPort());\n        remoteServerCheckBox.setSelected(EngineFactory.getLastRemote());\n\n        localDatabaseField.disableProperty().bind(remoteServerCheckBox.selectedProperty());\n        portField.disableProperty().bind(remoteServerCheckBox.selectedProperty().not());\n        databaseServerField.disableProperty().bind(remoteServerCheckBox.selectedProperty().not());\n        selectFileButton.disableProperty().bind(remoteServerCheckBox.selectedProperty());\n    }\n\n    @FXML\n    private void cancelAction() {\n        ((Stage) localDatabaseField.getScene().getWindow()).close();\n    }\n\n    @FXML\n    private void okAction() {\n        ((Stage) localDatabaseField.getScene().getWindow()).close();\n\n        if (remoteServerCheckBox.isSelected() || localDatabaseField.getText().length() > 0) {\n            BootEngineTask.initiateBoot(localDatabaseField.getText(), passwordField.getText().toCharArray(),\n                    remoteServerCheckBox.isSelected(), databaseServerField.getText(), portField.getInteger());\n        }\n    }\n\n    private void setDatabaseField(final String database) {\n        localDatabaseField.setText(database);\n        localDatabaseField.setTooltip(new Tooltip(database));\n    }\n\n    @FXML\n    protected void handleSelectFileAction() {\n        final File file = DatabasePathAction.getFileToOpen();\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(OpenDatabaseController.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            setDatabaseField(file.getAbsolutePath());\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/AbstractTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport java.time.LocalDate;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.RadioButton;\nimport javafx.scene.control.Spinner;\n\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.util.NotNull;\n\n/**\n * Abstract repeating reminder controller.\n *\n * @author Craig Cavanaugh\n */\nclass AbstractTabController implements RecurringTabController {\n\n    @FXML\n    private RadioButton noEndDateToggleButton;\n\n    @FXML\n    private RadioButton dateToggleButton;\n\n    @FXML\n    private DatePickerEx endDatePicker;\n\n    @FXML\n    Spinner<Integer> numberSpinner;\n\n    Reminder reminder;\n\n    @FXML\n    void initialize() {\n\n        // bind enabled state\n        endDatePicker.disableProperty().bind(noEndDateToggleButton.selectedProperty());\n\n        noEndDateToggleButton.setSelected(true);\n    }\n\n    @Override\n    public Reminder getReminder() {\n        LocalDate endDate = null;\n\n        if (dateToggleButton.isSelected()) {\n            endDate = endDatePicker.getValue();\n        }\n\n        reminder.setIncrement(numberSpinner.getValue());\n        reminder.setEndDate(endDate);\n\n        return reminder;\n    }\n\n    @Override\n    public void setReminder(@NotNull final Reminder reminder) {\n        this.reminder = reminder;\n\n        numberSpinner.getValueFactory().setValue(reminder.getIncrement());\n\n        if (reminder.getEndDate() != null) {\n            endDatePicker.setValue(reminder.getEndDate());\n            dateToggleButton.setSelected(true);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/DayTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.SpinnerValueFactory;\n\nimport jgnash.engine.recurring.DailyReminder;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.util.NotNull;\n\n/**\n * Daily repeating reminder controller.\n *\n * @author Craig Cavanaugh\n */\npublic class DayTabController extends AbstractTabController {\n\n    @FXML\n    void initialize() {\n\n        super.initialize();\n\n        reminder = new DailyReminder();\n\n        numberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 365, 1, 1));\n    }\n\n    @Override\n    public void setReminder(@NotNull final Reminder reminder) {\n        if (!(reminder instanceof DailyReminder)) {\n            throw new RuntimeException(\"Incorrect Reminder type\");\n        }\n\n        super.setReminder(reminder);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/MonthTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport java.util.ResourceBundle;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.SpinnerValueFactory;\n\nimport jgnash.engine.recurring.MonthlyReminder;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.util.NotNull;\n\n/**\n * Weekly repeating reminder controller.\n *\n * @author Craig Cavanaugh\n */\npublic class MonthTabController extends AbstractTabController {\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private ComboBox<String> typeComboBox;\n\n    @FXML\n    void initialize() {\n        super.initialize();\n\n        reminder = new MonthlyReminder();\n\n        numberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 24, 1, 1));\n\n        typeComboBox.getItems().addAll(resources.getString(\"Column.Date\"), resources.getString(\"Column.Day\"));\n        typeComboBox.getSelectionModel().select(0);\n    }\n\n    @Override\n    public Reminder getReminder() {\n        ((MonthlyReminder) reminder).setType(typeComboBox.getSelectionModel().getSelectedIndex());\n\n        return super.getReminder();\n    }\n\n    @Override\n    public void setReminder(@NotNull final Reminder reminder) {\n        if (!(reminder instanceof MonthlyReminder)) {\n            throw new RuntimeException(\"Incorrect Reminder type\");\n        }\n\n        super.setReminder(reminder);\n\n        typeComboBox.getSelectionModel().select(((MonthlyReminder) reminder).getType());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/NoneTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport jgnash.engine.recurring.OneTimeReminder;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.util.NotNull;\n\n/**\n * None repeating reminder controller.\n *\n * @author Craig Cavanaugh\n */\npublic class NoneTabController implements RecurringTabController {\n\n    private Reminder reminder = new OneTimeReminder();\n\n    @Override\n    public Reminder getReminder() {\n        return reminder;\n    }\n\n    @Override\n    public void setReminder(@NotNull final Reminder reminder) {\n        if (!(reminder instanceof OneTimeReminder)) {\n            throw new RuntimeException(\"Incorrect Reminder type\");\n        }\n\n        this.reminder = reminder;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/NotificationDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Collection;\nimport java.util.ResourceBundle;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.cell.CheckBoxTableCell;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.message.ChannelEvent;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.recurring.PendingReminder;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.TimePeriodComboBox;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * A dialog for displaying recurring event / transactions when they occur.\n *\n * @author Craig Cavanaugh\n */\nclass NotificationDialog extends Stage implements MessageListener {\n\n    @FXML\n    private Button cancelButton;\n\n    @FXML\n    private Button okButton;\n\n    @FXML\n    private Button selectAllButton;\n\n    @FXML\n    private Button clearAllButton;\n\n    @FXML\n    private Button invertButton;\n\n    @FXML\n    private TableView<PendingReminder> tableView;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private TimePeriodComboBox snoozeComboBox;\n\n    private final ObservableList<PendingReminder> observableReminderList = FXCollections.observableArrayList();\n\n\n    NotificationDialog() {\n        FXMLUtils.loadFXML(this, \"NotificationDialog.fxml\", ResourceUtils.getBundle());\n        setTitle(ResourceUtils.getString(\"Title.Reminder\"));\n    }\n\n    void setReminders(final Collection<PendingReminder> pendingReminders) {\n        observableReminderList.setAll(pendingReminders);\n    }\n\n    Collection<PendingReminder> getApprovedReminders() {\n        return observableReminderList.stream().filter(PendingReminder::isApproved).collect(Collectors.toList());\n    }\n\n    @FXML\n    private void initialize() {\n        final TableColumn<PendingReminder, Boolean> enabledColumn = new TableColumn<>(resources.getString(\"Column.Approve\"));\n        enabledColumn.setCellValueFactory(param -> new SimpleBooleanProperty(param.getValue().isApproved()));\n        enabledColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enabledColumn));\n        tableView.getColumns().add(enabledColumn);\n\n        final TableColumn<PendingReminder, LocalDate> dateColumn = new TableColumn<>(resources.getString(\"Column.Date\"));\n        dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getCommitDate()));\n        dateColumn.setCellFactory(cell -> new DateTableCell());\n        tableView.getColumns().add(dateColumn);\n\n        final TableColumn<PendingReminder, String> descriptionColumn = new TableColumn<>(resources.getString(\"Column.Description\"));\n        descriptionColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getReminder().getDescription()));\n        tableView.getColumns().add(descriptionColumn);\n\n        tableView.setItems(observableReminderList);\n\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        // Toggle the selection\n        tableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                newValue.setApproved(!newValue.isApproved());\n                tableView.refresh();\n                JavaFXUtils.runLater(() -> tableView.getSelectionModel().clearSelection());\n            }\n        });\n\n        okButton.onActionProperty().set(event -> handleOkayAction());\n        cancelButton.onActionProperty().set(event -> handleCancelAction());\n        selectAllButton.onActionProperty().set(event -> handleSelectAllAction());\n        clearAllButton.onActionProperty().set(event -> handleClearAllAction());\n        invertButton.onActionProperty().set(event -> handleInvertSelectionAction());\n\n        // configure the combo box and bind the property\n        snoozeComboBox.setSelectedPeriod(Options.reminderSnoozePeriodProperty().get());\n        Options.reminderSnoozePeriodProperty().bind(snoozeComboBox.periodProperty());\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM);\n\n        // unregister the listener when closing\n        setOnHiding(event -> MessageBus.getInstance().unregisterListener(NotificationDialog.this,\n                MessageChannel.SYSTEM));\n    }\n\n    private void handleSelectAllAction() {\n        for (final PendingReminder pendingReminder : observableReminderList) {\n            pendingReminder.setApproved(true);\n        }\n        tableView.refresh();\n    }\n\n    private void handleClearAllAction() {\n        for (final PendingReminder pendingReminder : observableReminderList) {\n            pendingReminder.setApproved(false);\n        }\n        tableView.refresh();\n    }\n\n    private void handleInvertSelectionAction() {\n        for (final PendingReminder pendingReminder : observableReminderList) {\n            pendingReminder.setApproved(!pendingReminder.isApproved());\n        }\n        tableView.refresh();\n    }\n\n    private void handleCancelAction() {\n        observableReminderList.clear(); // dump the list so caller sees nothing\n        close();\n    }\n\n    private void handleOkayAction() {\n        close();\n    }\n\n\n    @Override\n    public void messagePosted(final Message message) {\n        if (message.getEvent() == ChannelEvent.FILE_CLOSING) {  // close the dialog automatically\n            handleCancelAction();   // cancel and pending changes because close was forced\n        }\n    }\n\n    private static class DateTableCell extends TableCell<PendingReminder, LocalDate> {\n        private final DateTimeFormatter formatter = DateUtils.getShortDateFormatter();\n\n        @Override\n        protected void updateItem(final LocalDate date, final boolean empty) {\n            super.updateItem(date, empty);  // required\n\n            if (!empty && date != null) {\n                setText(formatter.format(date));\n            } else {\n                setText(null);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/RecurringDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\n\n/**\n * Dialog Controller for Recurring transactions.\n *\n * @author Craig Cavanaugh\n */\npublic class RecurringDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private StackPane contentPane;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        contentPane.getChildren()\n                .add(FXMLUtils.load(RecurringViewController.class.getResource(\"RecurringView.fxml\"), resources));\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/RecurringEntryDialog.java",
    "content": "package jgnash.uifx.views.recurring;\n\nimport java.util.Optional;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\n\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * A dialog for displaying recurring event / transactions when they occur.\n *\n * @author Craig Cavanaugh\n */\npublic class RecurringEntryDialog {\n\n    private final ObjectProperty<RecurringPropertiesController> controller = new SimpleObjectProperty<>();\n\n    private RecurringEntryDialog(final Reminder reminder) {\n        final FXMLUtils.Pair<RecurringPropertiesController> pair =\n                FXMLUtils.load(RecurringPropertiesController.class.getResource(\"RecurringProperties.fxml\"),\n                        reminder != null ? ResourceUtils.getString(\"Title.ModifyReminder\")\n                                : ResourceUtils.getString(\"Title.NewReminder\"));\n\n        controller.set(pair.getController());\n\n        if (reminder != null) {\n            controller.get().showReminder(reminder);\n        }\n\n        pair.getStage().setResizable(false);\n\n        StageUtils.addBoundsListener(pair.getStage(), RecurringEntryDialog.class, MainView.getPrimaryStage());\n\n        pair.getStage().showAndWait();  // must block the UI so the return value is generated correctly\n    }\n\n    public static Optional<Reminder> showAndWait(final Reminder reminder) {\n        final RecurringEntryDialog dialog = new RecurringEntryDialog(reminder);\n        return dialog.controller.get().getReminder();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/RecurringPropertiesController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.*;\nimport javafx.stage.Stage;\nimport jgnash.engine.Account;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.recurring.*;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.views.register.TransactionDialog;\nimport jgnash.time.DateUtils;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\n\n/**\n * Controller for creating and modifying a reminder.\n *\n * @author Craig Cavanaugh\n */\npublic class RecurringPropertiesController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button okButton;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private DatePickerEx startDatePicker;\n\n    @FXML\n    private TabPane tabs;\n\n    @FXML\n    private CheckBox enabledCheckBox;\n\n    @FXML\n    private TextField lastOccurrenceTextField;\n\n    @FXML\n    private CheckBox autoEnterCheckBox;\n\n    @FXML\n    private IntegerTextField daysBeforeTextField;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    @FXML\n    private TextField descriptionTextField;\n\n    @FXML\n    private TextField payeeTextField;\n\n    @FXML\n    private TextArea notesTextArea;\n\n    private Transaction transaction;\n\n    private final DateTimeFormatter dateFormatter = DateUtils.getShortDateFormatter();\n\n    private final HashMap<Class<?>, Integer> tabMap = new HashMap<>();\n\n    private Reminder reminder = null;\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        tabMap.put(OneTimeReminder.class, 0);\n        tabMap.put(DailyReminder.class, 1);\n        tabMap.put(WeeklyReminder.class, 2);\n        tabMap.put(MonthlyReminder.class, 3);\n        tabMap.put(YearlyReminder.class, 4);\n\n        daysBeforeTextField.disableProperty().bind(autoEnterCheckBox.selectedProperty().not());\n\n        enabledCheckBox.setSelected(true);  // enable by default\n\n        // ensure the description if not empty\n        okButton.disableProperty().bind(descriptionTextField.textProperty().isEmpty());\n\n        loadTab(\"NoneTab.fxml\", \"Tab.None\");\n        loadTab(\"DayTab.fxml\", \"Tab.Day\");\n        loadTab(\"WeekTab.fxml\", \"Tab.Week\");\n        loadTab(\"MonthTab.fxml\", \"Tab.Month\");\n        loadTab(\"YearTab.fxml\", \"Tab.Year\");\n    }\n\n    private void loadTab(final String fxml, final String name) {\n        final Tab tab = new Tab();\n        tab.setText(resources.getString(name));\n\n        final RecurringTabController controller = FXMLUtils.loadFXML(tab::setContent, fxml, resources);\n        tab.setUserData(controller);\n        tabs.getTabs().addAll(tab);\n    }\n\n    void showReminder(final Reminder reminder) {\n        final Account a = reminder.getAccount();\n\n        if (a != null) {\n            accountComboBox.setValue(a);\n        } else {\n            System.err.println(\"did not find account\");\n        }\n\n        transaction = reminder.getTransaction();\n\n        if (transaction != null) {\n            payeeTextField.setText(transaction.getPayee());\n        }\n\n        descriptionTextField.setText(reminder.getDescription());\n        enabledCheckBox.setSelected(reminder.isEnabled());\n        startDatePicker.setValue(reminder.getStartDate());\n        notesTextArea.setText(reminder.getNotes());\n\n        tabs.getSelectionModel().select(tabMap.get(reminder.getClass()));\n        ((RecurringTabController)tabs.getSelectionModel().getSelectedItem().getUserData()).setReminder(reminder);\n\n        if (reminder.getLastDate() != null) {\n            final LocalDate lastOccurrence = reminder.getLastDate();\n            lastOccurrenceTextField.setText(dateFormatter.format(lastOccurrence));\n        }\n\n        autoEnterCheckBox.setSelected(reminder.isAutoCreate());\n        daysBeforeTextField.setInteger(reminder.getDaysAdvance());\n    }\n\n    private Reminder generateReminder() {\n        final RecurringTabController tab = (RecurringTabController) tabs.getSelectionModel().getSelectedItem().getUserData();\n\n        final Reminder reminder = tab.getReminder();\n\n        reminder.setDescription(descriptionTextField.getText());\n        reminder.setEnabled(enabledCheckBox.isSelected());\n        reminder.setStartDate(startDatePicker.getValue());\n        reminder.setNotes(notesTextArea.getText());\n\n        reminder.setAutoCreate(autoEnterCheckBox.isSelected());\n\n        reminder.setDaysAdvance(daysBeforeTextField.getInteger());\n        reminder.setAccount(accountComboBox.getValue());\n        reminder.setTransaction(transaction);\n\n        return reminder;\n    }\n\n    public Optional<Reminder> getReminder() {\n        return Optional.ofNullable(reminder);\n    }\n\n    @FXML\n    private void okAction() {\n        reminder = generateReminder();\n\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void cancelAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleDeleteTransaction() {\n        transaction = null;\n        payeeTextField.setText(null);\n    }\n\n    @FXML\n    private void handleEditTransaction() {\n        TransactionDialog.showAndWait(accountComboBox.getValue(), this.transaction, transaction -> {\n            if (transaction != null) {\n                this.transaction = transaction;\n                payeeTextField.setText(this.transaction.getPayee());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/RecurringTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport jgnash.engine.recurring.Reminder;\n\n/**\n * Interface for a recurring tab panel.\n *\n * @author Craig Cavanaugh\n */\ninterface RecurringTabController {\n\n    /**\n     * Returns the {@code Reminder} for this tab.\n     *\n     * @return return {@code Reminder}\n     */\n    Reminder getReminder();\n\n    /**\n     * Sets the {@code Reminder} for this tab.\n     *\n     * @param reminder new {@code Reminder}\n     */\n    void setReminder(Reminder reminder);\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/RecurringViewController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.cell.CheckBoxTableCell;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.recurring.PendingReminder;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.ShortDateTableCell;\nimport jgnash.uifx.control.TableViewEx;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.LogUtil;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.logging.Logger;\n\n/**\n * Controller for recurring events.\n *\n * @author Craig Cavanaugh\n */\npublic class RecurringViewController implements MessageListener {\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private Button modifyButton;\n\n    @FXML\n    private Button nowButton;\n\n    @FXML\n    private TableViewEx<Reminder> tableView;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final ObservableList<Reminder> observableReminderList = FXCollections.observableArrayList();\n\n    private final SortedList<Reminder> sortedReminderList = new SortedList<>(observableReminderList);\n\n    private final ReadOnlyObjectWrapper<Reminder> selectedReminder = new ReadOnlyObjectWrapper<>();\n\n    private Timer timer = null;\n\n    private static final int START_UP_DELAY = 45 * 1000;   // 45 seconds\n\n    private final AtomicBoolean dialogShowing = new AtomicBoolean(false);\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Number> snoozePeriodListener;\n\n    @FXML\n    private void initialize() {\n        tableView.setClipBoardStringFunction(this::reminderToExcel);\n\n        tableView.setTableMenuButtonVisible(true);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        // hide the horizontal scrollbar and prevent ghosting\n        tableView.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS);\n\n        final TableColumn<Reminder, String> descriptionColumn = new TableColumn<>(resources.getString(\"Column.Description\"));\n        descriptionColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getDescription()));\n        tableView.getColumns().add(descriptionColumn);\n\n        final TableColumn<Reminder, String> accountColumn = new TableColumn<>(resources.getString(\"Column.Account\"));\n        accountColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getDescription()));\n        accountColumn.setCellValueFactory(param -> {\n            if (param.getValue().getAccount() != null) {\n                return new SimpleObjectProperty<>(param.getValue().getAccount().toString());\n            }\n            return null;\n        });\n        tableView.getColumns().add(accountColumn);\n\n        final TableColumn<Reminder, BigDecimal> amountColumn = new TableColumn<>(resources.getString(\"Column.Amount\"));\n        amountColumn.setCellValueFactory(param -> {\n            if (param.getValue().getTransaction() != null) {\n                return new SimpleObjectProperty<>(param.getValue().getTransaction().getAmount(param.getValue().getAccount()));\n            }\n            return null;\n        });\n\n        amountColumn.setCellFactory(param -> new ReminderBigDecimalTableCell());\n        tableView.getColumns().add(amountColumn);\n\n        final TableColumn<Reminder, String> frequencyColumn = new TableColumn<>(resources.getString(\"Column.Freq\"));\n        frequencyColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getReminderType().toString()));\n        tableView.getColumns().add(frequencyColumn);\n\n        final TableColumn<Reminder, Boolean> enabledColumn = new TableColumn<>(resources.getString(\"Column.Enabled\"));\n        enabledColumn.setCellValueFactory(param -> new SimpleBooleanProperty(param.getValue().isEnabled()));\n        enabledColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enabledColumn));\n        tableView.getColumns().add(enabledColumn);\n\n        final TableColumn<Reminder, LocalDate> lastPosted = new TableColumn<>(resources.getString(\"Column.LastPosted\"));\n        lastPosted.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getLastDate()));\n        lastPosted.setCellFactory(cell -> new ShortDateTableCell<>());\n        tableView.getColumns().add(lastPosted);\n\n        final TableColumn<Reminder, LocalDate> due = new TableColumn<>(resources.getString(\"Column.Due\"));\n        due.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getIterator().next()));\n        due.setCellFactory(cell -> new ShortDateTableCell<>());\n        tableView.getColumns().add(due);\n\n        sortedReminderList.comparatorProperty().bind(tableView.comparatorProperty());\n\n        tableView.setItems(sortedReminderList);\n\n        selectedReminder.bind(tableView.getSelectionModel().selectedItemProperty());\n\n        // bind enabled state of the buttons to the selected reminder property\n        deleteButton.disableProperty().bind(Bindings.isNull(selectedReminder));\n        modifyButton.disableProperty().bind(Bindings.isNull(selectedReminder));\n        nowButton.disableProperty().bind(Bindings.isNull(selectedReminder));\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM, MessageChannel.REMINDER);\n\n        JavaFXUtils.runLater(this::loadTable);\n\n        startTimer(true);\n\n        // Update the period when the snooze value changes\n        snoozePeriodListener = (observable, oldValue, newValue) -> {\n            stopTimer();\n\n            // Don't start the timer if the dialog is up, otherwise events may get backed up\n            // while the dialog is up.\n            if (!dialogShowing.get()) {\n                startTimer(false);\n            }\n        };\n\n        Options.reminderSnoozePeriodProperty().addListener(new WeakChangeListener<>(snoozePeriodListener));\n    }\n\n    private String reminderToExcel(final Reminder reminder) {\n        final StringBuilder builder = new StringBuilder();\n        final DateTimeFormatter dateFormatter = DateUtils.getExcelDateFormatter();\n\n        builder.append(reminder.getDescription());\n        builder.append('\\t');\n        builder.append(reminder.getAccount().getName());\n        builder.append('\\t');\n        builder.append(reminder.getTransaction().getAmount(reminder.getAccount()).toPlainString());\n        builder.append('\\t');\n        builder.append(reminder.getReminderType());\n        builder.append('\\t');\n        builder.append(reminder.isEnabled());\n        builder.append('\\t');\n        builder.append(dateFormatter.format(reminder.getLastDate()));\n        builder.append('\\t');\n        if (reminder.getIterator().next() != null)\n        {\n            builder.append(dateFormatter.format(reminder.getIterator().next()));\n        }\n\n        return builder.toString();\n    }\n\n    private void loadTable() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        Objects.requireNonNull(engine);\n\n        observableReminderList.setAll(engine.getReminders());\n    }\n\n    /**\n     * Starts the timer\n     *\n     * @param startup should be true for initialization and false for restarts\n     */\n    private void startTimer(final boolean startup) {\n        if (timer == null) {\n            timer = new Timer(true);\n\n            timer.schedule(new TimerTask() {\n                               @Override\n                               public void run() {\n                                   JavaFXUtils.runLater(RecurringViewController.this::showReminderDialog);\n                               }\n                           }, startup ? START_UP_DELAY : Options.reminderSnoozePeriodProperty().get(),\n                    Options.reminderSnoozePeriodProperty().get());\n\n            Logger.getLogger(RecurringViewController.class.getName()).info(\"Recurring timer started: \"\n                    + Options.reminderSnoozePeriodProperty().get() / 1000 + \" secs\");\n        }\n    }\n\n    private void stopTimer() {\n        if (timer != null) {\n            timer.cancel();\n            timer = null;\n\n            Logger.getLogger(RecurringViewController.class.getName()).info(\"Recurring timer stopped\");\n        }\n    }\n\n    private void showReminderDialog() {\n        Logger.getLogger(RecurringViewController.class.getName()).info(\"Show dialog\");\n\n        if (dialogShowing.compareAndSet(false, true)) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (engine != null) {   // make sure were not in process of a shutdown\n                final Collection<PendingReminder> pendingReminders = engine.getPendingReminders();\n\n                if (!pendingReminders.isEmpty()) {\n\n                    // Stop the timer so events don't back up if the dialog is up too long.\n                    stopTimer();\n\n                    final NotificationDialog notificationDialog = new NotificationDialog();\n\n                    notificationDialog.setReminders(pendingReminders);\n                    notificationDialog.showAndWait();\n\n                    engine.processPendingReminders(notificationDialog.getApprovedReminders());\n\n                    startTimer(false);\n                }\n\n                dialogShowing.getAndSet(false);\n            }\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        switch (message.getEvent()) {\n            case FILE_CLOSING:\n                stopTimer();\n                observableReminderList.removeAll();\n                MessageBus.getInstance().unregisterListener(this, MessageChannel.SYSTEM, MessageChannel.REMINDER);\n                break;\n            case REMINDER_ADD:\n            case REMINDER_UPDATE:\n            case REMINDER_REMOVE:\n                loadTable();\n                break;\n            default:\n        }\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        if (selectedReminder.get() != null) {\n            if (Options.confirmOnDeleteReminderProperty().get()) {\n                if (StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.Confirm\"),\n                        resources.getString(\"Message.ConfirmReminderDelete\"))\n                        .getButtonData() != ButtonBar.ButtonData.YES) {\n                    return;\n                }\n            }\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.removeReminder(selectedReminder.get());\n        }\n    }\n\n    @FXML\n    private void handleRefreshAction() {\n        showReminderDialog();\n    }\n\n    @FXML\n    private void handleNewAction() {\n        final Optional<Reminder> optional = RecurringEntryDialog.showAndWait(null);\n\n        optional.ifPresent(reminder -> {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            if (!engine.addReminder(reminder)) {\n                StaticUIMethods.displayError(resources.getString(\"Message.Error.ReminderAdd\"));\n            }\n        });\n    }\n\n    @FXML\n    private void handleModifyAction() {\n\n        final Reminder old = selectedReminder.get();\n\n        try {\n            final Optional<Reminder> optional = RecurringEntryDialog.showAndWait((Reminder) old.clone());\n\n            optional.ifPresent(reminder -> {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                if (engine.removeReminder(old)) { // remove the old\n                    if (!engine.addReminder(reminder)) { // add the new\n                        StaticUIMethods.displayError(resources.getString(\"Message.Error.ReminderUpdate\"));\n                    }\n                } else {\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.ReminderUpdate\"));\n                }\n\n            });\n        } catch (final CloneNotSupportedException e) {\n            LogUtil.logSevere(RecurringViewController.class, e);\n        }\n    }\n\n    @FXML\n    private void handleNowAction() {\n        if (selectedReminder.get().isEnabled()) {\n            if (StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.Confirm\"),\n                    resources.getString(\"Message.Confirm.ExecuteReminder\"))\n                    .getButtonData() != ButtonBar.ButtonData.YES) {\n                return;\n            }\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            final PendingReminder pendingReminder = Engine.getPendingReminder(selectedReminder.get());\n\n            if (pendingReminder != null) {\n                pendingReminder.setApproved(true);\n                engine.processPendingReminders(Collections.singletonList(pendingReminder));\n            }\n        }\n    }\n\n    private static class ReminderBigDecimalTableCell extends TableCell<Reminder, BigDecimal> {\n        {\n            setStyle(\"-fx-alignment: center-right;\");\n        }\n\n        @Override\n        protected void updateItem(final BigDecimal amount, final boolean empty) {\n            super.updateItem(amount, empty);\n\n            if (!empty && getTableRow() != null && getTableRow().getItem() != null) {\n                final Reminder reminder = getTableRow().getItem();\n\n                if (reminder.getTransaction() != null) {\n                    setText(NumericFormats.getFullCommodityFormat(reminder.getAccount().getCurrencyNode())\n                            .format(amount));\n\n                    if (amount != null && amount.signum() < 0) {\n                        setId(StyleClass.NORMAL_NEGATIVE_CELL_ID);\n                    } else {\n                        setId(StyleClass.NORMAL_CELL_ID);\n                    }\n                } else {\n                    setText(NumericFormats.getFullCommodityFormat(reminder.getAccount().getCurrencyNode())\n                            .format(BigDecimal.ZERO));\n                }\n            } else {\n                setText(null);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/WeekTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.SpinnerValueFactory;\n\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.engine.recurring.WeeklyReminder;\nimport jgnash.util.NotNull;\n\n/**\n * Weekly repeating reminder controller.\n *\n * @author Craig Cavanaugh\n */\npublic class WeekTabController extends AbstractTabController {\n\n    @FXML\n    void initialize() {\n        super.initialize();\n\n        reminder = new WeeklyReminder();\n\n        numberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 52, 1, 1));\n    }\n\n    @Override\n    public void setReminder(@NotNull final Reminder reminder) {\n        if (!(reminder instanceof WeeklyReminder)) {\n            throw new RuntimeException(\"Incorrect Reminder type\");\n        }\n\n        super.setReminder(reminder);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/recurring/YearTabController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.recurring;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.SpinnerValueFactory;\n\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.engine.recurring.YearlyReminder;\nimport jgnash.util.NotNull;\n\n/**\n * Yearly repeating reminder controller.\n *\n * @author Craig Cavanaugh\n */\npublic class YearTabController extends AbstractTabController {\n\n    @FXML\n    void initialize() {\n        super.initialize();\n\n        reminder = new YearlyReminder();\n\n        numberSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 100, 1, 1));\n    }\n\n    @Override\n    public void setReminder(@NotNull final Reminder reminder) {\n        if (!(reminder instanceof YearlyReminder)) {\n            throw new RuntimeException(\"Incorrect Reminder type\");\n        }\n\n        super.setReminder(reminder);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractInvIncomeSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.AbstractInvestmentTransactionEntry;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionTag;\nimport jgnash.engine.TransactionType;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AutoCompleteTextField;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.SecurityComboBox;\nimport jgnash.uifx.control.TransactionNumberComboBox;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\n\n/**\n * Abstract investment income entry controller.\n *\n * @author Craig Cavanaugh\n */\npublic abstract class AbstractInvIncomeSlipController extends AbstractInvSlipController {\n\n    @FXML\n    DatePickerEx datePicker;\n\n    @FXML\n    AutoCompleteTextField<Transaction> memoTextField;\n\n    @FXML\n    TransactionNumberComboBox numberComboBox;\n\n    @FXML\n    SecurityComboBox securityComboBox;\n\n    @FXML\n    DecimalTextField decimalTextField; // dividend field\n\n    @FXML\n    AccountExchangePane accountExchangePane;\n\n    @FXML\n    AccountExchangePane incomeExchangePane;\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    private static final Logger logger = MainView.getLogger();\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        // Lazy init when account property is set\n        account.addListener((observable, oldValue, newValue) -> {\n            decimalTextField.scaleProperty().set(newValue.getCurrencyNode().getScale());\n            decimalTextField.minScaleProperty().set(newValue.getCurrencyNode().getScale());\n\n            accountExchangePane.baseCurrencyProperty().set(accountProperty().get().getCurrencyNode());\n            incomeExchangePane.baseCurrencyProperty().set(accountProperty().get().getCurrencyNode());\n\n            accountExchangePane.amountProperty().bindBidirectional(decimalTextField.decimalProperty());\n            incomeExchangePane.amountProperty().bindBidirectional(decimalTextField.decimalProperty());\n\n            clearForm();\n        });\n\n        securityComboBox.accountProperty().bind(account);\n\n        validFormProperty.bind(Bindings\n                .isNotNull(securityComboBox.valueProperty())\n                .and(decimalTextField.textProperty().isNotEmpty())\n        );\n    }\n\n    @Override\n    protected void focusFirstComponent() {\n        securityComboBox.requestFocus();\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull Transaction transaction) {\n        if (transaction.getTransactionType() != getTransactionType()) {\n            throw new IllegalArgumentException(resources.getString(\"Message.Error.InvalidTransactionType\"));\n        }\n\n        clearForm();\n\n        datePicker.setValue(transaction.getLocalDate());\n        numberComboBox.setValue(transaction.getNumber());\n\n        final List<TransactionEntry> entries = transaction.getTransactionEntries();\n\n        for (final TransactionEntry e : entries) {\n            if (e instanceof AbstractInvestmentTransactionEntry\n                    && ((AbstractInvestmentTransactionEntry) e).getTransactionType() == getTransactionType()) {\n\n                memoTextField.setText(e.getMemo());\n                securityComboBox.setSecurityNode(((AbstractInvestmentTransactionEntry)e).getSecurityNode());\n\n                incomeExchangePane.setSelectedAccount(e.getDebitAccount());\n                incomeExchangePane.setExchangedAmount(e.getDebitAmount().abs());\n\n                decimalTextField.setDecimal(e.getAmount(accountProperty().get()));\n            } else if (e.getTransactionTag() == TransactionTag.INVESTMENT_CASH_TRANSFER) {\n                accountExchangePane.setSelectedAccount(e.getCreditAccount());\n                accountExchangePane.setExchangedAmount(e.getCreditAmount());\n            } else {\n                logger.warning(\"Invalid transaction\");\n            }\n        }\n\n        modTrans = transaction;\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        setReconciledState(transaction.getReconciled(accountProperty().get()));\n    }\n\n    @NotNull\n    abstract TransactionType getTransactionType();\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        if (!Options.rememberLastDateProperty().get()) {\n            datePicker.setValue(LocalDate.now());\n        }\n\n        numberComboBox.setValue(\"\");\n        memoTextField.clear();\n        decimalTextField.setDecimal(BigDecimal.ZERO);\n\n        accountExchangePane.setSelectedAccount(accountProperty().get());\n        incomeExchangePane.setSelectedAccount(accountProperty().get());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractInvSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Parent;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.layout.ColumnConstraints;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.util.NotNull;\n\n/**\n * Base Slip controller for investment transactions\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractInvSlipController implements Slip {\n\n    @InjectFXML\n    private final ObjectProperty<Parent> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    ResourceBundle resources;\n\n    @FXML\n    private CheckBox reconciledButton;\n\n    @FXML\n    DatePickerEx datePicker;\n\n    @FXML\n    TransactionTagPane tagPane;\n\n    @FXML\n    protected ColumnConstraints dateColumnConstraint;\n\n    /**\n     * Holds a reference to a transaction being modified.\n     */\n    Transaction modTrans = null;\n\n    final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    final BooleanProperty validFormProperty = new SimpleBooleanProperty();\n\n    void initialize() {\n\n        // Needed to support tri-state capability\n        reconciledButton.setAllowIndeterminate(true);\n\n        // Lazy init when account property is set\n        account.addListener((observable, oldValue, newValue) -> {\n            if (!newValue.memberOf(AccountGroup.INVEST)) {\n                throw new RuntimeException(resources.getString(\"Message.Error.InvalidAccountGroup\"));\n            }\n        });\n\n        // Install an event handler when the parent has been set via injection\n        parent.addListener((observable, oldValue, newValue) -> installKeyPressedHandler(newValue));\n\n        if (dateColumnWidth.get() == 0) {\n            dateColumnWidth.bind(getDateColumnWidth(datePicker.getStyle()));\n        }\n\n        dateColumnConstraint.minWidthProperty().bindBidirectional(dateColumnWidth);\n        dateColumnConstraint.maxWidthProperty().bindBidirectional(dateColumnWidth);\n    }\n\n    @Override\n    public BooleanProperty validFormProperty() {\n        return validFormProperty;\n    }\n\n    @Override\n    @NotNull\n    public CheckBox getReconcileButton() {\n        return reconciledButton;\n    }\n\n    @Override\n    public void handleEnterAction() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        Objects.requireNonNull(engine);\n\n        if (modTrans == null) {\n            final Transaction newTrans = buildTransaction();\n\n            // Need to set the reconciled state\n            ReconcileManager.reconcileTransaction(accountProperty().get(), newTrans, getReconciledState());\n\n            engine.addTransaction(newTrans);\n        } else {\n            final Transaction newTrans = buildTransaction();\n\n                /* Need to preserve the reconciled state of the opposite side\n                 * if both sides are not automatically reconciled\n                 */\n            ReconcileManager.reconcileTransaction(accountProperty().get(), newTrans, getReconciledState());\n\n            if (engine.isTransactionValid(newTrans)) {\n                if (engine.removeTransaction(modTrans)) {\n                    engine.addTransaction(newTrans);\n                }\n            }\n        }\n        clearForm();\n        focusFirstComponent();\n    }\n\n    @Override\n    public void handleCancelAction() {\n        clearForm();\n        focusFirstComponent();\n    }\n\n    @Override\n    public void clearForm() {\n        modTrans = null;\n\n        reconciledButton.setDisable(false);\n        reconciledButton.setSelected(false);\n        reconciledButton.setIndeterminate(false);\n\n        tagPane.clearSelectedTags();\n    }\n\n    protected abstract void focusFirstComponent();\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractPriceQtyInvSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AutoCompleteTextField;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.SecurityComboBox;\nimport jgnash.uifx.control.TransactionNumberComboBox;\n\n/**\n * Base Investment Slip controller.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractPriceQtyInvSlipController extends AbstractInvSlipController {\n\n    @FXML\n    AutoCompleteTextField<Transaction> memoTextField;\n\n    @FXML\n    DecimalTextField quantityField;\n\n    @FXML\n    DecimalTextField priceField;\n\n    @FXML\n    TransactionNumberComboBox numberComboBox;\n\n    @FXML\n    SecurityComboBox securityComboBox;\n\n    @FXML\n    DecimalTextField totalField;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        quantityField.scaleProperty().set(MathConstants.SECURITY_QUANTITY_ACCURACY);\n        priceField.scaleProperty().set(MathConstants.SECURITY_PRICE_ACCURACY);\n\n        totalField.setEditable(false);\n        totalField.setFocusTraversable(false);\n\n        // Lazy init when account property is set\n        account.addListener((observable, oldValue, newValue) -> {\n            priceField.minScaleProperty().set(newValue.getCurrencyNode().getScale());\n\n            totalField.scaleProperty().set(newValue.getCurrencyNode().getScale());\n            totalField.minScaleProperty().set(newValue.getCurrencyNode().getScale());\n        });\n\n        securityComboBox.accountProperty().bind(account);\n\n        validFormProperty.bind(Bindings\n                .isNotNull(securityComboBox.valueProperty())\n                .and(totalField.textProperty().isNotEmpty())\n        );\n    }\n\n    @Override\n    protected void focusFirstComponent() {\n        priceField.requestFocus();\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        if (!Options.rememberLastDateProperty().get()) {\n            datePicker.setValue(LocalDate.now());\n        }\n\n        numberComboBox.setValue(\"\");\n        memoTextField.clear();\n        priceField.setDecimal(BigDecimal.ZERO);\n        quantityField.setDecimal(BigDecimal.ZERO);\n        totalField.setDecimal(BigDecimal.ZERO);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.Parent;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.layout.ColumnConstraints;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.AutoCompleteTextField;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.TransactionNumberComboBox;\nimport jgnash.uifx.control.autocomplete.AutoCompleteFactory;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.util.NotNull;\n\n/**\n * Abstract bank transaction entry slip controller.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractSlipController implements Slip {\n\n    @InjectFXML\n    private final ObjectProperty<Parent> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    protected DecimalTextField amountField;\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    @FXML\n    protected DatePickerEx datePicker;\n\n    @FXML\n    protected AutoCompleteTextField<Transaction> memoTextField;\n\n    @FXML\n    protected TransactionNumberComboBox numberComboBox;\n\n    @FXML\n    protected AutoCompleteTextField<Transaction> payeeTextField;\n\n    @FXML\n    protected TransactionTagPane tagPane;\n\n    @FXML\n    private CheckBox reconciledButton;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    protected ColumnConstraints dateColumnConstraint;\n\n    final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Boolean> focusChangeListener;\n\n    /**\n     * Holds a reference to a transaction being modified.\n     */\n    Transaction modTrans = null;\n\n    @FXML\n    ResourceBundle resources;\n\n    final BooleanProperty validFormProperty = new SimpleBooleanProperty();\n\n    @FXML\n    public void initialize() {\n\n        // May not have a button bar\n        if (buttonBar != null) {\n            buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n        }\n\n        // Needed to support tri-state capability\n        reconciledButton.setAllowIndeterminate(true);\n\n        // Number combo needs to know the account in order to determine the next transaction number\n        numberComboBox.accountProperty().bind(accountProperty());\n\n        AutoCompleteFactory.setMemoModel(memoTextField);\n\n        account.addListener((observable, oldValue, newValue) -> {\n            // Set the number of fixed decimal places for entry\n            amountField.scaleProperty().set(newValue.getCurrencyNode().getScale());\n\n            // Enabled auto completion for the payee field\n            if (payeeTextField != null) {   // transfer slips do not use the payee field\n                AutoCompleteFactory.setPayeeModel(payeeTextField, newValue);\n            }\n        });\n\n        // If focus is lost, check and load the form with an existing transaction\n        if (payeeTextField != null) {   // transfer slips do not use the payee field\n\n            focusChangeListener = (observable, oldValue, newValue) -> {\n                if (!newValue) {\n                    handlePayeeFocusChange();\n                }\n            };\n\n            payeeTextField.focusedProperty().addListener(new WeakChangeListener<>(focusChangeListener));\n        }\n\n        // Install an event handler when the parent has been set via injection\n        parent.addListener((observable, oldValue, newValue) -> installKeyPressedHandler(newValue));\n\n        validFormProperty.bind(amountField.validDecimalProperty());\n\n        if (dateColumnWidth.get() == 0) {\n            dateColumnWidth.bind(getDateColumnWidth(datePicker.getStyle()));\n        }\n\n        dateColumnConstraint.minWidthProperty().bindBidirectional(dateColumnWidth);\n        dateColumnConstraint.maxWidthProperty().bindBidirectional(dateColumnWidth);\n    }\n\n    @Override\n    public BooleanProperty validFormProperty() {\n        return validFormProperty;\n    }\n\n    @Override\n    @NotNull\n    public CheckBox getReconcileButton() {\n        return reconciledButton;\n    }\n\n    /**\n     * Determines is this form can be used to modify a transaction.\n     *\n     * @param transaction {@code Transaction} to confirm\n     * @return {@code true} if the {@code Transaction} can be modified\n     */\n    abstract boolean canModifyTransaction(final Transaction transaction);\n\n    @Override\n    public void clearForm() {\n        modTrans = null;\n\n        amountField.setDecimal(BigDecimal.ZERO);\n\n        reconciledButton.setDisable(false);\n        reconciledButton.setSelected(false);\n        reconciledButton.setIndeterminate(false);\n\n        if (payeeTextField != null) {    // transfer slips do not use the payee field\n            payeeTextField.setEditable(true);\n            payeeTextField.clear();\n        }\n\n        if (tagPane != null) {\n            tagPane.clearSelectedTags();\n        }\n\n        datePicker.setEditable(true);\n        if (!Options.rememberLastDateProperty().get()) {\n            datePicker.setValue(LocalDate.now());\n        }\n\n        memoTextField.clear();\n\n        numberComboBox.setValue(null);\n        numberComboBox.setDisable(false);\n\n        attachmentPane.clear();\n    }\n\n    ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    @FXML\n    @Override\n    public void handleCancelAction() {\n        clearForm();\n        focusFirstComponent();\n    }\n\n    @FXML\n    @Override\n    public void handleEnterAction() {\n        Transaction newTrans = buildTransaction();\n\n        if (modTrans == null) { // new transaction\n\n            ReconcileManager.reconcileTransaction(account.get(), newTrans, getReconciledState());\n\n            newTrans = attachmentPane.buildTransaction(newTrans);  // chain the transaction build\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (engine != null) {\n                if (!engine.addTransaction(newTrans)) {\n                    StaticUIMethods.displayError(resources.getString(\"Message.Error.TranAddFail\"));\n                }\n            }\n        } else {\n\n            // restore the reconciled state of the previous old transaction\n            for (final Account a : modTrans.getAccounts()) {\n                if (!a.equals(account.get())) {\n                    ReconcileManager.reconcileTransaction(a, newTrans, modTrans.getReconciled(a));\n                }\n            }\n\n            /*\n             * Reconcile the modified transaction for this account.\n             * This must be performed last to ensure consistent results per the ReconcileManager rules\n             */\n            ReconcileManager.reconcileTransaction(account.get(), newTrans, getReconciledState());\n\n            newTrans = attachmentPane.buildTransaction(newTrans);  // chain the transaction build\n\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            if (engine != null && engine.removeTransaction(modTrans)) {\n                engine.addTransaction(newTrans);\n            }\n        }\n\n        clearForm();\n        focusFirstComponent();\n    }\n\n    /**\n     * Focuses the first component the user will interact with.\n     */\n    void focusFirstComponent() {\n        if (payeeTextField != null) {\n            payeeTextField.requestFocus();\n        } else {\n            memoTextField.requestFocus();\n        }\n    }\n\n    private void handlePayeeFocusChange() {\n        if (modTrans == null && Options.useAutoCompleteProperty().get() && payeeTextField.getLength() > 0) {\n            if (payeeTextField.autoCompleteModelObjectProperty().get() != null) {\n\n                // The auto complete model may return multiple solutions.  Choose the first solution that works\n                final List<Transaction> transactions = new ArrayList<>(payeeTextField.autoCompleteModelObjectProperty()\n                                                                               .get().getAllExtraInfo(payeeTextField.getText()));\n\n                Collections.reverse(transactions);  // reverse the transactions, most recent first\n\n                for (final Transaction transaction : transactions) {\n                    if (canModifyTransaction(transaction)) {\n                        try {\n                            modifyTransaction(modifyTransactionForAutoComplete((Transaction) transaction.clone()));\n                        } catch (final CloneNotSupportedException e) {\n                            Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);\n                        }\n                        modTrans = null; // clear the modTrans field  TODO: use new transaction instead?\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Modify a transaction before it is used to complete the panel for auto fill. The supplied transaction must be a\n     * new or cloned transaction. It can't be a transaction that lives in the map. The returned transaction can be the\n     * supplied reference or may be a new instance\n     *\n     * @param t The transaction to modify\n     * @return the modified transaction\n     */\n    private Transaction modifyTransactionForAutoComplete(final Transaction t) {\n\n        // tweak the transaction\n        t.setNumber(null);\n        t.setReconciled(ReconciledState.NOT_RECONCILED); // clear both sides\n\n        // set the last date as required\n        if (!Options.rememberLastDateProperty().get()) {\n            t.setDate(LocalDate.now());\n        } else {\n            t.setDate(datePicker.getValue());\n        }\n\n        // preserve any transaction entries that may have been entered first\n        if (amountField.getLength() > 0) {\n            Transaction newTrans = buildTransaction();\n            t.clearTransactionEntries();\n            t.addTransactionEntries(newTrans.getTransactionEntries());\n        }\n\n        // preserve any preexisting memo field info\n        if (memoTextField.getLength() > 0) {\n            t.setMemo(memoTextField.getText());\n        }\n\n        // Do not copy over attachments\n        t.setAttachment(null);\n\n        return t;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractTransactionEntryDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.stage.Stage;\nimport javafx.stage.WindowEvent;\nimport javafx.util.Callback;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.util.TableViewManager;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\n\n/**\n * Abstract dialog for split transactions.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractTransactionEntryDialog extends Stage {\n\n    private static final double[] PREF_COLUMN_WEIGHTS = {50, 50, 0, 0, 0, 0};\n\n    @FXML\n    private Button newButton;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private Button deleteAllButton;\n\n    @FXML\n    private Button closeButton;\n\n    @FXML\n    TableView<TransactionEntry> tableView;\n\n    @FXML\n    ResourceBundle resources;\n\n    private final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private TableViewManager<TransactionEntry> tableViewManager;\n\n    private final ObservableList<TransactionEntry> transactionEntries = FXCollections.observableArrayList();\n\n    private final SortedList<TransactionEntry> sortedList = new SortedList<>(transactionEntries);\n\n    abstract String getPrefNode();\n\n    /**\n     * This will be called after the dialog is closed.\n     */\n    private Runnable closeRunnable;\n\n    ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    ObservableList<TransactionEntry> getTransactionEntries() {\n        return transactionEntries;\n    }\n\n    @FXML\n    private void initialize() {\n        accountProperty().addListener((observable, oldValue, newValue) -> {\n            initForm();\n            loadTable();\n        });\n\n        tableView.setTableMenuButtonVisible(true);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        // Modify a {@code TransactionEntry} on selection\n        tableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) { // null can occur when the transaction entry list changes\n                modifyTransactionEntry(newValue);\n            }\n        });\n\n        // repack when the list contents change and this dialog is showing\n        transactionEntries.addListener((ListChangeListener<TransactionEntry>) c -> {\n\n            // If the list changes, clear the selection\n            tableView.getSelectionModel().clearSelection();\n\n            // Repack when the list contents change and this dialog is showing\n            if (isShowing()) {\n                tableViewManager.packTable();\n            }\n\n            // Force a table view refresh, running totals will need to be recalculated\n            JavaFXUtils.runLater(() -> tableView.refresh());\n        });\n\n        closeButton.setOnAction(event -> closeAction());\n        deleteButton.setOnAction(event -> deleteAction());\n        deleteAllButton.setOnAction(event -> deleteAllAction());\n        newButton.setOnAction(event -> newAction());\n\n        addEventHandler(WindowEvent.WINDOW_SHOWN, event -> {\n            tableViewManager.restoreLayout();   // restore layout and pack after the table is visible\n        });\n    }\n\n    abstract void initForm();\n\n    abstract void newAction();\n\n    abstract void deleteAction();\n\n    abstract void modifyTransactionEntry(@NotNull final TransactionEntry transactionEntry);\n\n    /**\n     * Delete all of the transaction entries.\n     */\n    private void deleteAllAction() {\n        tableView.getSelectionModel().clearSelection();\n        transactionEntries.clear();\n    }\n\n    private void loadTable() {\n        tableViewManager = new TableViewManager<>(tableView, getPrefNode());\n        tableViewManager.setColumnWeightFactory(getColumnWeightFactory());\n        tableViewManager.setPreferenceKeyFactory(() -> accountProperty().get().getUuid().toString());\n\n        sortedList.comparatorProperty().bind(tableView.comparatorProperty());\n\n        buildTable();\n\n        tableView.setItems(sortedList);\n    }\n\n    private Callback<Integer, Double> getColumnWeightFactory() {\n        return param -> PREF_COLUMN_WEIGHTS[param];\n    }\n\n    String[] getSplitColumnName() {\n        return RegisterFactory.getSplitColumnNames(accountProperty().get().getAccountType());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private void buildTable() {\n        final String[] columnNames = getSplitColumnName();\n\n        final TableColumn<TransactionEntry, String> accountColumn = new TableColumn<>(columnNames[0]);\n        accountColumn.setCellValueFactory(param -> new AccountNameWrapper(param.getValue()));\n\n        final TableColumn<TransactionEntry, String> reconciledColumn = new TableColumn<>(columnNames[1]);\n        reconciledColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().\n                getReconciled(account.get()).toString()));\n\n        final TableColumn<TransactionEntry, String> memoColumn = new TableColumn<>(columnNames[2]);\n        memoColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getMemo()));\n\n        final Callback<TableColumn<TransactionEntry, BigDecimal>, TableCell<TransactionEntry, BigDecimal>> cellFactory =\n                cell -> new TransactionEntryCommodityFormatTableCell(NumericFormats.getShortCommodityFormat(account.get().getCurrencyNode()));\n\n        final TableColumn<TransactionEntry, BigDecimal> increaseColumn = new TableColumn<>(columnNames[3]);\n        increaseColumn.setCellValueFactory(param -> new IncreaseAmountProperty(param.getValue().\n                getAmount(accountProperty().getValue())));\n\n        increaseColumn.setCellFactory(cellFactory);\n\n        final TableColumn<TransactionEntry, BigDecimal> decreaseColumn = new TableColumn<>(columnNames[4]);\n        decreaseColumn.setCellValueFactory(param -> new DecreaseAmountProperty(param.getValue().\n                getAmount(accountProperty().getValue())));\n        decreaseColumn.setCellFactory(cellFactory);\n\n        final TableColumn<TransactionEntry, BigDecimal> balanceColumn = new TableColumn<>(columnNames[5]);\n        balanceColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(getBalanceAt(param.getValue())));\n        balanceColumn.setCellFactory(cell -> new TransactionEntryCommodityFormatTableCell(NumericFormats.\n                getFullCommodityFormat(account.get().getCurrencyNode())));\n        balanceColumn.setSortable(false);   // do not allow a sort on the balance\n\n        tableView.getColumns().addAll(memoColumn, accountColumn, reconciledColumn, increaseColumn, decreaseColumn,\n                balanceColumn);\n\n        tableViewManager.setColumnFormatFactory(param -> {\n            if (param == balanceColumn) {\n                return NumericFormats.getFullCommodityFormat(accountProperty().getValue().getCurrencyNode());\n            } else if (param == increaseColumn || param == decreaseColumn) {\n                return NumericFormats.getShortCommodityFormat(accountProperty().getValue().getCurrencyNode());\n            }\n\n            return null;\n        });\n    }\n\n    private BigDecimal getBalanceAt(final TransactionEntry transactionEntry) {\n        BigDecimal balance = BigDecimal.ZERO;\n\n        final Account account = this.account.get();\n\n        if (account != null) {\n            final int index = sortedList.indexOf(transactionEntry);\n\n            for (int i = 0; i <= index; i++) {\n                balance = balance.add(sortedList.get(i).getAmount(account));\n            }\n        }\n        return balance;\n    }\n\n    private void closeAction() {\n        closeButton.getScene().getWindow().hide();\n\n        // call the runnable to indicate closure if not null\n        if (closeRunnable != null) {\n            closeRunnable.run();\n        }\n    }\n\n    private class AccountNameWrapper extends SimpleStringProperty {\n        AccountNameWrapper(final TransactionEntry t) {\n            super();\n\n            final Account creditAccount = t.getCreditAccount();\n\n            if (creditAccount != accountProperty().get()) {\n                setValue(creditAccount.getName());\n            } else {\n                setValue(t.getDebitAccount().getName());\n            }\n        }\n    }\n\n    BigDecimal getBalance() {\n        if (sortedList.isEmpty()) {\n            return BigDecimal.ZERO;\n        }\n\n        return getBalanceAt(sortedList.get(sortedList.size() - 1));\n    }\n\n    void show(final Runnable runnable) {\n        closeRunnable = runnable;\n\n        // Reset bounds before showing\n        StageUtils.addBoundsListener(this, getClass(), MainView.getPrimaryStage());\n\n        super.show();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractTransactionEntrySlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Parent;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AutoCompleteTextField;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.autocomplete.AutoCompleteFactory;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.util.NotNull;\n\n/**\n * Abstract controller for spit transaction entries of various types.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractTransactionEntrySlipController implements BaseSlip {\n\n    @InjectFXML\n    private final ObjectProperty<Parent> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    DecimalTextField amountField;\n\n    @FXML\n    AutoCompleteTextField<Transaction> memoField;\n\n    @FXML\n    AccountExchangePane accountExchangePane;\n\n    @FXML\n    private CheckBox reconciledButton;\n\n    @FXML\n    TransactionTagPane tagPane;\n\n    @FXML\n    AttachmentPane attachmentPane;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private ResourceBundle resources;\n\n    final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<List<TransactionEntry>> transactionEntryList = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Comparator<TransactionEntry>> comparator = new SimpleObjectProperty<>();\n\n    private final BooleanProperty validFormProperty = new SimpleBooleanProperty();\n\n    TransactionEntry oldEntry;\n\n    @FXML\n    protected void initialize() {\n        if (buttonBar != null) {\n            buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n        }\n\n        reconciledButton.setAllowIndeterminate(true);\n\n        // Bind necessary properties to the exchange panel\n        accountExchangePane.baseAccountProperty().bind(accountProperty());\n        accountExchangePane.amountProperty().bindBidirectional(amountField.decimalProperty());\n        accountExchangePane.amountEditableProperty().bind(amountField.editableProperty());\n\n        // Enabled auto completion\n        AutoCompleteFactory.setMemoModel(memoField);\n\n        // Install an event handler when the parent has been set via injection\n        parent.addListener((observable, oldValue, newValue) -> installKeyPressedHandler(newValue));\n\n        validFormProperty.bind(amountField.textProperty().isNotEmpty());\n    }\n\n    @Override\n    public BooleanProperty validFormProperty() {\n        return validFormProperty;\n    }\n\n    @Override\n    @NotNull\n    public CheckBox getReconcileButton() {\n        return reconciledButton;\n    }\n\n    abstract TransactionEntry buildTransactionEntry();\n\n    ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    ObjectProperty<List<TransactionEntry>> transactionEntryListProperty() {\n        return transactionEntryList;\n    }\n\n    ObjectProperty<Comparator<TransactionEntry>> comparatorProperty() {\n        return comparator;\n    }\n\n    boolean hasEqualCurrencies() {\n        return account.get().getCurrencyNode().equals(accountExchangePane.getSelectedAccount().getCurrencyNode());\n    }\n\n    public void clearForm() {\n        oldEntry = null;\n\n        reconciledButton.setDisable(false);\n        reconciledButton.setSelected(false);\n        reconciledButton.setIndeterminate(false);\n\n        memoField.setText(null);\n        amountField.setDecimal(BigDecimal.ZERO);\n        accountExchangePane.setExchangedAmount(null);\n\n        if (tagPane != null) {\n            tagPane.clearSelectedTags();\n        }\n    }\n\n    @FXML\n    public void handleEnterAction() {\n\n        final TransactionEntry entry = buildTransactionEntry();\n\n        final List<TransactionEntry> entries = transactionEntryList.get();\n\n        if (oldEntry != null) {\n            entries.remove(oldEntry);\n        }\n\n        final int index = Collections.binarySearch(entries, entry, comparator.get());\n\n        if (index < 0) {\n            entries.add(-index - 1, entry);\n        }\n\n        clearForm();\n        memoField.requestFocus();\n    }\n\n    @FXML\n    public void handleCancelAction() {\n        clearForm();\n        memoField.requestFocus();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AbstractTransactionTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\n\nimport javafx.scene.control.TableCell;\n\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.skin.StyleClass;\n\n/**\n * Abstract {@code TableCell} for rendering numeric transaction values.\n *\n * @author Craig Cavanaugh\n */\nabstract class AbstractTransactionTableCell extends TableCell<Transaction, BigDecimal> {\n\n    AbstractTransactionTableCell() {\n\n        // Right align numeric values\n        setStyle(\"-fx-alignment: center-right;\");\n    }\n\n    void applyFormat(final BigDecimal amount, final NumberFormat format) {\n        setText(format.format(amount));\n\n        // Not empty and amount is not null, but tableRow can be null... JavaFx Bug?\n        if (getTableRow() != null && getTableRow().getItem() != null) {\n            final boolean future = getTableRow().getItem().getLocalDate().isAfter(LocalDate.now());\n            final boolean negative = amount.signum() < 0;\n\n            // Set font style\n            if (future && negative) {\n                setId(StyleClass.ITALIC_NEGATIVE_CELL_ID);\n            } else if (future) {\n                setId(StyleClass.ITALIC_CELL_ID);\n            } else if (negative) {\n                setId(StyleClass.NORMAL_NEGATIVE_CELL_ID);\n            } else {\n                setId(StyleClass.NORMAL_CELL_ID);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AccountExchangePane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.MathConstants;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.PopOverButton;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Controller for handling the exchange of currencies.\n *\n * @author Craig Cavanaugh\n */\npublic class AccountExchangePane extends GridPane {\n\n    @FXML\n    private AccountComboBox accountCombo;\n\n    @FXML\n    private DecimalTextField exchangeAmountField;\n\n    @FXML\n    private DecimalTextField exchangeRateField;\n\n    @FXML\n    private PopOverButton expandButton;\n\n    @FXML\n    private Label label;\n\n    @FXML\n    private Label exchangeLabel;\n\n    /**\n     * Account property may be null.\n     */\n    final private ObjectProperty<Account> baseAccount = new SimpleObjectProperty<>();\n\n    final private ObjectProperty<CurrencyNode> baseCurrency = new SimpleObjectProperty<>();\n\n    final private ObjectProperty<BigDecimal> exchangeAmount = new SimpleObjectProperty<>(BigDecimal.ZERO);\n\n    final private ObjectProperty<BigDecimal> amount = new SimpleObjectProperty<>(BigDecimal.ZERO);\n\n    final private SimpleBooleanProperty amountEditable = new SimpleBooleanProperty();\n\n    final private ObjectProperty<Account> selectedAccount = new SimpleObjectProperty<>();\n\n    /**\n     * Determines if the base account will be visible for selection.\n     */\n    final private SimpleBooleanProperty filterBaseAccount = new SimpleBooleanProperty(true);\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Boolean> amountFocusChangeListener;\n\n    /**\n     * Reference is needed to prevent premature garbage collection.\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Boolean> rateFocusChangeListener;\n\n    public AccountExchangePane() {\n        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"AccountExchangePane.fxml\"),\n                ResourceUtils.getBundle());\n\n        fxmlLoader.setRoot(this);\n        fxmlLoader.setController(this);\n\n        try {\n            fxmlLoader.load();\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @FXML\n    private void initialize() {\n\n        accountCombo.disableProperty().bind(disableProperty());\n        expandButton.disableProperty().bind(disableProperty());\n        exchangeRateField.disableProperty().bind(disableProperty());\n\n        exchangeRateField.scaleProperty().set(MathConstants.EXCHANGE_RATE_ACCURACY);\n\n        // base currency should always match the account if the account is set\n        baseAccount.addListener((observable, oldValue, newValue) -> {\n            if (baseCurrency.get() != null && newValue.getCurrencyNode() != baseCurrency.get()) {\n                throw new RuntimeException(\"baseCurrency does not match baseAccount currency\");\n            }\n\n            if (filterBaseAccountProperty().get()) {\n                // Set predicate to filter the base account\n                accountCombo.setPredicate(AccountComboBox.getDefaultPredicate().and(account -> !account.equals(newValue)));\n            }\n            baseCurrencyProperty().set(newValue.getCurrencyNode());\n        });\n\n        baseCurrency.addListener((observable, oldValue, newValue) -> {\n            if (baseAccount.get() != null && baseAccount.get().getCurrencyNode() != newValue) {\n                throw new RuntimeException(\"baseCurrency does not match baseAccount currency\");\n            }\n\n            updateExchangeRateField();\n            updateControlVisibility();\n        });\n\n        accountCombo.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                exchangeAmountField.scaleProperty().set(newValue.getCurrencyNode().getScale());\n                updateExchangeRateField();\n                updateControlVisibility();\n            }\n        });\n\n        selectedAccountProperty().bindBidirectional(accountCombo.valueProperty());\n\n        // update the exchange label text\n        expandButton.focusedProperty().addListener((observable, oldValue, newValue) -> {\n            // protect against an NPE / race condition at application shutdown should the accountCombo have purged\n            // all values prior to a focus change\n            if (accountCombo.getValue() != null) {\n                exchangeLabel.setText(NumericFormats.getConversion(baseCurrency.get(),\n                        accountCombo.getValue().getCurrencyNode()));\n            }\n        });\n\n        exchangeAmount.bindBidirectional(exchangeAmountField.decimalProperty());\n\n        amountFocusChangeListener = (observable, oldValue, newValue) -> {\n            if (!newValue) {\n                if (exchangeAmountField.getDecimal().compareTo(BigDecimal.ZERO) > 0) {\n                    exchangeAmountFieldAction();\n                }\n            }\n        };\n\n        exchangeAmountField.focusedProperty().addListener(new WeakChangeListener<>(amountFocusChangeListener));\n\n        // Call exchangeRateFieldAction() on entry or loss of focus\n        exchangeRateField.setOnAction(event -> exchangeRateFieldAction());\n\n        rateFocusChangeListener = (observable, oldValue, newValue) -> {\n            if (!newValue) {\n                exchangeRateFieldAction();\n            }\n        };\n\n        exchangeRateField.focusedProperty().addListener(new WeakChangeListener<>(rateFocusChangeListener));\n\n        amount.addListener((observable, oldValue, newValue) -> amountFieldAction());\n    }\n\n    private void updateExchangeRateField() {\n        final Account selectedAccount = accountCombo.getValue();\n\n        if (selectedAccount != null && baseCurrency.get() != null) {\n            exchangeRateField.setDecimal(baseCurrency.get().getExchangeRate(selectedAccount.getCurrencyNode()));\n        }\n    }\n\n    private BigDecimal getAmount() {\n        if (amount.get() != null) {\n            return amount.get();\n        }\n        \n\t\treturn BigDecimal.ZERO;\n    }\n\n    private void amountFieldAction() {\n        if (exchangeRateField.getDecimal().compareTo(BigDecimal.ZERO) == 0) {\n            if (getAmount().compareTo(BigDecimal.ZERO) != 0) {\n                exchangeRateField.setDecimal(exchangeAmount.get().divide(getAmount(),\n                        MathConstants.mathContext));\n            }\n        } else {\n            exchangeAmount.set(getAmount().multiply(exchangeRateField.getDecimal(),\n                    MathConstants.mathContext).setScale(baseCurrency.get().getScale(),\n                    MathConstants.roundingMode));\n        }\n    }\n\n    private void exchangeRateFieldAction() {\n        if (exchangeAmount.get() != null) { // NPE if exchangeAmount has not been set\n            if (getAmount().compareTo(BigDecimal.ZERO) == 0 && amountEditable.get()) {\n                if (exchangeRateField.getDecimal().compareTo(BigDecimal.ZERO) != 0) {\n                    amount.set(exchangeAmount.get().divide(exchangeRateField.getDecimal(),\n                            MathConstants.mathContext).setScale(baseCurrency.get().getScale(),\n                            MathConstants.roundingMode));\n                }\n            } else {\n                exchangeAmount.set(getAmount().multiply(exchangeRateField.getDecimal(),\n                        MathConstants.mathContext).setScale(baseCurrency.get().getScale(),\n                        MathConstants.roundingMode));\n            }\n        }\n    }\n\n    private void exchangeAmountFieldAction() {\n        if (getAmount().compareTo(BigDecimal.ZERO) == 0 && amountEditable.get()) {\n            if (exchangeRateField.getDecimal().compareTo(BigDecimal.ZERO) != 0) {\n                amount.set(exchangeAmount.get().divide(exchangeRateField.getDecimal(),\n                        MathConstants.mathContext).setScale(baseCurrency.get().getScale(),\n                        MathConstants.roundingMode));\n            }\n        } else {\n            if (getAmount().compareTo(BigDecimal.ZERO) != 0) {\n                exchangeRateField.setDecimal(exchangeAmount.get().divide(getAmount(),\n                        MathConstants.mathContext));\n            }\n        }\n    }\n\n    private void updateControlVisibility() {\n        if (getSelectedAccount() != null && baseCurrencyProperty() != null) {\n            if (getSelectedAccount().getCurrencyNode() == baseCurrencyProperty().get()) {\n                getChildren().removeAll(label, exchangeAmountField, expandButton);\n                GridPane.setColumnSpan(accountCombo, 4);    // span the empty columns\n            } else {\n                if (!getChildren().contains(label)) {\n                    GridPane.setColumnSpan(accountCombo, 1);    // reduce the column span before adding the nodes back\n                    getChildren().addAll(label, exchangeAmountField, expandButton);\n                }\n            }\n        }\n    }\n\n    ObjectProperty<Account> baseAccountProperty() {\n        return baseAccount;\n    }\n\n    ObjectProperty<Account> selectedAccountProperty() {\n        return selectedAccount;\n    }\n\n    ObjectProperty<CurrencyNode> baseCurrencyProperty() {\n        return baseCurrency;\n    }\n\n    ObjectProperty<BigDecimal> exchangeAmountProperty() {\n        return exchangeAmount;\n    }\n\n    BigDecimal getExchangeRate() {\n        return exchangeRateField.getDecimal();\n    }\n\n    ObjectProperty<BigDecimal> amountProperty() {\n        return amount;\n    }\n\n    SimpleBooleanProperty amountEditableProperty() {\n        return amountEditable;\n    }\n\n    public Account getSelectedAccount() {\n        return accountCombo.getValue();\n    }\n\n    void setSelectedAccount(final Account account) {\n        accountCombo.setValue(account);\n    }\n\n    void setExchangedAmount(final BigDecimal value) {\n        exchangeAmount.set(value);\n    }\n\n    SimpleBooleanProperty filterBaseAccountProperty() {\n        return filterBaseAccount;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AccountPropertyWrapper.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyStringProperty;\nimport javafx.beans.property.ReadOnlyStringWrapper;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.concurrent.Task;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.util.DefaultDaemonThreadFactory;\n\n/**\n * Account properties wrapper to making binding of long-running processes easier.\n *\n * @author Craig Cavanaugh\n */\nclass AccountPropertyWrapper implements MessageListener {\n\n    private static final ExecutorService executorService =\n            Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory(\"Account Property Wrapper Executor\"));\n\n    private final Object numberFormatLock = new Object();\n\n    private final ReadOnlyStringWrapper reconciledAmount = new ReadOnlyStringWrapper();\n\n    private final ReadOnlyStringWrapper accountBalance = new ReadOnlyStringWrapper();\n\n    private final ReadOnlyStringWrapper cashBalance = new ReadOnlyStringWrapper();\n\n    private final ReadOnlyStringWrapper marketValue = new ReadOnlyStringWrapper();\n\n    private final ReadOnlyStringWrapper accountName = new ReadOnlyStringWrapper();\n\n    private final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private NumberFormat numberFormat;  // not thread safe\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private final ChangeListener<String> formatListener;\n\n    AccountPropertyWrapper() {\n        MessageBus.getInstance().registerListener(this, MessageChannel.ACCOUNT, MessageChannel.TRANSACTION);\n\n        account.addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n\n                // Account changed, update the number format\n                synchronized (numberFormatLock) {\n                    numberFormat = NumericFormats.getFullCommodityFormat(newValue.getCurrencyNode());\n                }\n\n                // Update account properties\n                updateProperties();\n            }\n        });\n\n        // Listen for changes to formatting preferences and force an update\n        formatListener = (observable, oldValue, newValue) -> {\n            synchronized (numberFormatLock) {\n                numberFormat = NumericFormats.getFullCommodityFormat(account.get().getCurrencyNode());\n                updateProperties();\n            }\n        };\n\n        Options.fullNumericFormatProperty().addListener(new WeakChangeListener<>(formatListener));\n        Options.shortNumericFormatProperty().addListener(new WeakChangeListener<>(formatListener));\n        Options.shortDateFormatProperty().addListener(new WeakChangeListener<>(formatListener));\n    }\n\n    @Override\n    public void messagePosted(final Message event) {\n        switch (event.getEvent()) {\n            case ACCOUNT_MODIFY:\n            case TRANSACTION_ADD:\n            case TRANSACTION_REMOVE:\n                if (event.getObject(MessageProperty.ACCOUNT).equals(account.get())) {\n                    updateProperties();\n                }\n                break;\n            default:\n        }\n    }\n\n    private void updateProperties() {\n        if (account.get() != null) {\n            JavaFXUtils.runLater(() -> accountName.set(account.get().getName()));\n        } else {\n            JavaFXUtils.runLater(() -> accountName.set(\"\"));\n        }\n\n        executorService.submit(new Task<Void>() {\n            @Override\n            protected Void call() {\n                synchronized (numberFormatLock) {\n                    if (account.get() != null) {\n                        final Account acc = AccountPropertyWrapper.this.account.get();\n                        final BigDecimal balance =\n                                AccountBalanceDisplayManager.convertToSelectedBalanceMode(acc.getAccountType(),\n                                        acc.getBalance());\n\n                        JavaFXUtils.runLater(() -> accountBalance.set(numberFormat.format(balance)));\n                    } else {\n                        JavaFXUtils.runLater(() -> accountBalance.set(numberFormat.format(BigDecimal.ZERO)));\n                    }\n                }\n                return null;\n            }\n        });\n\n        executorService.submit(new Task<Void>() {\n            @Override\n            protected Void call() {\n                synchronized (numberFormatLock) {\n                    if (account.get() != null) {\n                        final Account acc = AccountPropertyWrapper.this.account.get();\n                        final BigDecimal balance =\n                                AccountBalanceDisplayManager.convertToSelectedBalanceMode(acc.getAccountType(),\n                                        acc.getReconciledBalance());\n\n                        JavaFXUtils.runLater(() -> reconciledAmount.set(numberFormat.format(balance)));\n                    } else {\n                        JavaFXUtils.runLater(() -> reconciledAmount.set(numberFormat.format(BigDecimal.ZERO)));\n                    }\n                }\n                return null;\n            }\n        });\n\n        if (account.get() != null && account.get().memberOf(AccountGroup.INVEST)) {\n            executorService.submit(new Task<Void>() {\n                @Override\n                protected Void call() {\n                    synchronized (numberFormatLock) {\n                        if (account.get() != null) {\n                            final Account acc = AccountPropertyWrapper.this.account.get();\n                            final BigDecimal balance =\n                                    AccountBalanceDisplayManager.convertToSelectedBalanceMode(acc.getAccountType(),\n                                            acc.getCashBalance());\n\n                            JavaFXUtils.runLater(() -> cashBalance.set(numberFormat.format(balance)));\n                        } else {\n                            JavaFXUtils.runLater(() -> cashBalance.set(numberFormat.format(BigDecimal.ZERO)));\n                        }\n                    }\n                    return null;\n                }\n            });\n\n            executorService.submit(new Task<Void>() {\n                @Override\n                protected Void call() {\n                    synchronized (numberFormatLock) {\n                        if (account.get() != null) {\n                            JavaFXUtils.runLater(() -> marketValue.set(numberFormat.format(account.get().getMarketValue())));\n                        } else {\n                            JavaFXUtils.runLater(() -> marketValue.set(numberFormat.format(BigDecimal.ZERO)));\n                        }\n                    }\n                    return null;\n                }\n            });\n        }\n    }\n\n    ReadOnlyStringProperty reconciledAmountProperty() {\n        return reconciledAmount.getReadOnlyProperty();\n    }\n\n    ReadOnlyStringProperty accountBalanceProperty() {\n        return accountBalance.getReadOnlyProperty();\n    }\n\n    ReadOnlyStringProperty cashBalanceProperty() {\n        return cashBalance.getReadOnlyProperty();\n    }\n\n    ReadOnlyStringProperty marketValueProperty() {\n        return marketValue.getReadOnlyProperty();\n    }\n\n    ReadOnlyStringProperty accountNameProperty() {\n        return accountName.getReadOnlyProperty();\n    }\n\n    public ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AdjustSharesSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntryAddX;\nimport jgnash.engine.TransactionEntryRemoveX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Adding and Removing shares.\n *\n * @author Craig Cavanaugh\n */\npublic class AdjustSharesSlipController extends AbstractPriceQtyInvSlipController {\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    private TransactionType tranType = TransactionType.ADDSHARE;\n\n    @FXML\n    public void initialize() {\n        super.initialize();\n\n        accountProperty().addListener((observable, oldValue, newValue) -> clearForm());\n\n        final ChangeListener<BigDecimal> changeListener = (observable, oldValue, newValue) -> updateTotalField();\n\n        quantityField.decimalProperty().addListener(changeListener);\n        priceField.decimalProperty().addListener(changeListener);\n    }\n\n    void setTransactionType(final TransactionType tranType) {\n        this.tranType = tranType;\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (!(transaction instanceof InvestmentTransaction) ||\n                !(transaction.getTransactionType() == TransactionType.REMOVESHARE ||\n                transaction.getTransactionType() == TransactionType.ADDSHARE)) {\n            throw new IllegalArgumentException(resources.getString(\"Message.Error.InvalidTransactionType\"));\n        }\n\n        clearForm();\n\n        datePicker.setValue(transaction.getLocalDate());\n        memoTextField.setText(transaction.getTransactionMemo());\n        numberComboBox.setValue(transaction.getNumber());\n        priceField.setDecimal(((InvestmentTransaction)transaction).getPrice());\n        quantityField.setDecimal(((InvestmentTransaction)transaction).getQuantity());\n        securityComboBox.setSecurityNode(((InvestmentTransaction) transaction).getSecurityNode());\n\n        tagPane.setSelectedTags(transaction.getTags(tranType == TransactionType.ADDSHARE\n                                                            ? TransactionEntryAddX.class\n                                                            : TransactionEntryRemoveX.class));\n\n        modTrans = transaction;\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        setReconciledState(transaction.getReconciled(accountProperty().get()));\n    }\n\n    private void updateTotalField() {\n        totalField.setDecimal(quantityField.getDecimal().multiply(priceField.getDecimal()));\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n\n        final Transaction transaction;\n\n        if (tranType == TransactionType.ADDSHARE) {\n            transaction = TransactionFactory.generateAddXTransaction(accountProperty().get(), securityComboBox.getValue(),\n                    priceField.getDecimal(), quantityField.getDecimal(), datePicker.getValue(), memoTextField.getText());\n        } else {\n            transaction = TransactionFactory.generateRemoveXTransaction(accountProperty().get(), securityComboBox.getValue(),\n                    priceField.getDecimal(), quantityField.getDecimal(), datePicker.getValue(), memoTextField.getText());\n        }\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(tranType == TransactionType.ADDSHARE\n                                    ? TransactionEntryAddX.class\n                                    : TransactionEntryRemoveX.class, tagPane.getSelectedTags());\n\n        return attachmentPane.buildTransaction(transaction);\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        attachmentPane.clear();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AdjustmentSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.views.accounts.StaticAccountsMethods;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Credits and Debits.\n *\n * @author Craig Cavanaugh\n */\npublic class AdjustmentSlipController extends AbstractSlipController {\n\n    @FXML\n    private Button enterButton;\n\n    @FXML\n    private Button convertButton;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        enterButton.disableProperty().bind(validFormProperty.not());\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        Transaction transaction = TransactionFactory.generateSingleEntryTransaction(account.get(), amountField.getDecimal(),\n                datePicker.getValue(), memoTextField.getText(), payeeTextField.getText(), numberComboBox.getValue());\n\n        transaction.setTags(tagPane.getSelectedTags());\n\n        return transaction;\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (canModifyTransaction(transaction)) {\n            if (transaction.areAccountsLocked()) {\n                clearForm();\n                StaticUIMethods.displayError(resources.getString(\"Message.TransactionModifyLocked\"));\n                return;\n            }\n\n            newTransaction(transaction); // load the form\n\n            modTrans = transaction; // save reference to old transaction\n            modTrans = attachmentPane.modifyTransaction(modTrans);\n\n            convertButton.setDisable(false);\n        }\n    }\n\n    @Override\n    boolean canModifyTransaction(final Transaction t) {\n        return t.getTransactionType() == TransactionType.SINGLENTRY;\n    }\n\n    private void newTransaction(final Transaction t) {\n        clearForm();\n\n        amountField.setDecimal(t.getAmount(accountProperty().get()));\n\n        memoTextField.setText(t.getTransactionMemo());\n        payeeTextField.setText(t.getPayee());\n        numberComboBox.setValue(t.getNumber());\n\n        datePicker.setValue(t.getLocalDate());\n        setReconciledState(t.getReconciled(accountProperty().get()));\n\n        tagPane.setSelectedTags(t.getTags(account.get()));\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        convertButton.setDisable(true);\n    }\n\n    @FXML\n    private void convertAction() {\n        final Optional<Account> accountOptional = StaticAccountsMethods.selectAccount(null, account.get());\n\n        accountOptional.ifPresent(opp -> {\n            final Transaction t = new Transaction();\n\n            t.setDate(datePicker.getValue());\n            t.setNumber(numberComboBox.getValue());\n            t.setPayee(payeeTextField.getText());\n\n            final TransactionEntry entry = new TransactionEntry();\n            entry.setMemo(memoTextField.getText());\n\n            if (amountField.getDecimal().signum() >= 0) {\n                entry.setCreditAccount(account.get());\n                entry.setDebitAccount(opp);\n            } else {\n                entry.setDebitAccount(account.get());\n                entry.setCreditAccount(opp);\n            }\n\n            entry.setCreditAmount(amountField.getDecimal().abs());\n            entry.setDebitAmount(amountField.getDecimal().abs().negate());\n\n            t.addTransactionEntry(entry);\n\n            ReconcileManager.reconcileTransaction(account.get(), t, getReconciledState());\n\n            t.setTags(tagPane.getSelectedTags());\n\n            TransactionDialog.showAndWait(account.get(), t, transaction -> {\n                if (transaction != null) {\n\n                    final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                    Objects.requireNonNull(engine);\n\n                    if (engine.removeTransaction(modTrans)) {\n                        engine.addTransaction(transaction);\n                    }\n                    clearForm();\n                }\n            });\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AmortizeSetupDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.stage.Stage;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AmortizeObject;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.IntegerTextField;\nimport jgnash.uifx.control.TextFieldEx;\nimport jgnash.uifx.util.JavaFXUtils;\n\nimport java.math.BigDecimal;\nimport java.util.Locale;\n\n/**\n * Amortization setup controller\n *\n * @author Craig Cavanaugh\n */\npublic class AmortizeSetupDialogController {\n\n    @FXML\n    private AccountComboBox interestAccountCombo;\n\n    @FXML\n    private AccountComboBox bankAccountCombo;\n\n    @FXML\n    private AccountComboBox feesAccountCombo;\n\n    @FXML\n    private TextFieldEx payeeField;\n\n    @FXML\n    private TextFieldEx memoField;\n\n    @FXML\n    private DecimalTextField daysField;\n\n    @FXML\n    private DatePickerEx dateField;\n\n    @FXML\n    private DecimalTextField feesField;\n\n    @FXML\n    private CheckBox useDaysButton;\n\n    @FXML\n    private IntegerTextField payPeriodsField;\n\n    @FXML\n    private IntegerTextField intPeriodsField;\n\n    @FXML\n    private IntegerTextField loanTermField;\n\n    @FXML\n    private DecimalTextField loanAmountField;\n\n    @FXML\n    private DecimalTextField interestField;\n\n    private final Account NOP_ACCOUNT = new Account();\n\n    private boolean result;\n\n    @FXML\n    public void initialize() {\n\n        feesAccountCombo.setPredicate(AccountComboBox.getDefaultPredicate().and(account -> account == NOP_ACCOUNT\n                || account.memberOf(AccountGroup.EXPENSE)));\n\n        feesAccountCombo.getUnfilteredItems().add(0, NOP_ACCOUNT);\n\n        daysField.disableProperty().bind(useDaysButton.selectedProperty().not());\n\n        feesField.disableProperty().bind(Bindings.isNull(feesAccountCombo.valueProperty())\n                .or(Bindings.equal(NOP_ACCOUNT, feesAccountCombo.valueProperty())));\n\n        JavaFXUtils.runLater(() -> fillForm(generateDefault()));\n    }\n\n    AmortizeObject getAmortizeObject() {\n        AmortizeObject o = new AmortizeObject();\n\n        o.setDate(dateField.getValue());\n        o.setFees(feesField.getDecimal());\n        o.setInterestPeriods(intPeriodsField.getInteger());\n        o.setPaymentPeriods(payPeriodsField.getInteger());\n        o.setPrincipal(loanAmountField.getDecimal());\n        o.setRate(interestField.getDecimal());\n        o.setLength(loanTermField.getInteger());\n        o.setPayee(payeeField.getText());\n        o.setMemo(memoField.getText());\n        o.setUseDailyRate(useDaysButton.isSelected());\n        o.setDaysPerYear(daysField.getDecimal());\n\n\n        o.setBankAccount(bankAccountCombo.getValue());\n        o.setInterestAccount(interestAccountCombo.getValue());\n        o.setFeesAccount(feesAccountCombo.getValue() != NOP_ACCOUNT ? feesAccountCombo.getValue() : null);\n\n        return o;\n    }\n\n    void setAmortizeObject(final AmortizeObject amortizeObject) {\n        JavaFXUtils.runLater(() -> fillForm(amortizeObject));\n    }\n\n    private static AmortizeObject generateDefault() {\n        AmortizeObject o = new AmortizeObject();\n\n        // make up some reasonable numbers\n        o.setLength(360);\n        o.setPaymentPeriods(12);\n        o.setRate(new BigDecimal(\"4.75\"));\n        o.setPrincipal(new BigDecimal(\"80000.00\"));\n        o.setUseDailyRate(false);\n        o.setDaysPerYear(new BigDecimal(\"365\"));\n\n        // Defaults for US and CA are known... not sure about others\n        if (Locale.getDefault().getCountry().equals(\"CA\")) {\n            o.setInterestPeriods(4);\n        } else {\n            o.setInterestPeriods(12);\n        }\n        return o;\n    }\n\n    private void fillForm(final AmortizeObject ao) {\n        interestField.setDecimal(ao.getRate());\n        loanAmountField.setDecimal(ao.getPrincipal());\n        loanTermField.setInteger(ao.getLength());\n        payPeriodsField.setInteger(ao.getPaymentPeriods());\n        intPeriodsField.setInteger(ao.getInterestPeriods());\n        feesField.setDecimal(ao.getFees());\n        memoField.setText(ao.getMemo());\n        payeeField.setText(ao.getPayee());\n        dateField.setValue(ao.getDate());\n        daysField.setDecimal(ao.getDaysPerYear());\n        useDaysButton.setSelected(ao.getUseDailyRate());\n\n        Account a = ao.getBankAccount();\n        if (a != null) {\n            bankAccountCombo.setAccountValue(ao.getBankAccount());\n        }\n\n        a = ao.getInterestAccount();\n        if (a != null) {\n            interestAccountCombo.setAccountValue(ao.getInterestAccount());\n        }\n\n        feesAccountCombo.setAccountValue(feesAccountCombo.getValue() == null ? NOP_ACCOUNT : ao.getFeesAccount());\n    }\n\n    /**\n     * Returns true or false depending on if the user closed the dialog with\n     * the OK button or Cancel button.\n     *\n     * @return the closing state of the dialog\n     */\n    public boolean getResult() {\n        return result;\n    }\n\n    @FXML\n    private void okAction() {\n        result = true;\n        ((Stage) memoField.getScene().getWindow()).close();\n    }\n\n    @FXML\n    private void cancelAction() {\n        ((Stage) memoField.getScene().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/AttachmentPane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javax.imageio.ImageIO;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.FileChooser;\n\nimport jgnash.engine.AttachmentUtils;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.ImageDialog;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.FileUtils;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Controller for handling transaction attachments.\n *\n * @author Craig Cavanaugh\n */\npublic class AttachmentPane extends GridPane {\n\n    private static final String LAST_DIR = \"LastDir\";\n\n    @FXML\n    protected Button attachmentButton;\n\n    @FXML\n    protected Button viewAttachmentButton;\n\n    @FXML\n    protected Button deleteAttachmentButton;\n\n    private final SimpleObjectProperty<Path> attachment = new SimpleObjectProperty<>(null);\n\n    private boolean moveAttachment = false;\n\n    @FXML\n    private ResourceBundle resources;\n\n    public AttachmentPane() {\n        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"AttachmentPane.fxml\"),\n                ResourceUtils.getBundle());\n\n        fxmlLoader.setRoot(this);\n        fxmlLoader.setController(this);\n\n        try {\n            fxmlLoader.load();\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @FXML\n    private void initialize() {\n        attachmentButton.disableProperty().bind(Bindings.isNotNull(attachment));\n        deleteAttachmentButton.disableProperty().bind(Bindings.isNull(attachment));\n        viewAttachmentButton.disableProperty().bind(Bindings.isNull(attachment));\n\n        attachmentButton.setOnAction(event -> attachmentAction());\n        deleteAttachmentButton.setOnAction(event -> handleDeleteAction());\n        viewAttachmentButton.setOnAction(event -> showImageAction());\n    }\n\n    private void handleDeleteAction() {\n        attachment.set(null);\n    }\n\n    /**\n     * Builder method for extracting an attachment from an existing {@code Transaction}.\n     *\n     * @param transaction {@code Transaction} to extract attachment information from\n     * @return the provided {@code Transaction}\n     */\n    Transaction modifyTransaction(final Transaction transaction) {\n        new Thread(() -> {\n            if (transaction.getAttachment() != null && !transaction.getAttachment().isEmpty()) {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                final Future<Path> pathFuture = engine.getAttachment(transaction.getAttachment());\n\n                JavaFXUtils.runLater(() -> {\n                    try {\n                        attachment.set(pathFuture.get());\n                    } catch (final InterruptedException | ExecutionException e) {\n                        Logger.getLogger(AttachmentPane.class.getName()).log(Level.SEVERE, e.getMessage(), e);\n                    }\n                });\n            }\n        }).start();\n\n        return transaction;\n    }\n\n    /**\n     * Builder method for binding an attachment to a {@code Transaction}.\n     *\n     * @param transaction {@code Transaction} to update\n     * @return the provided {@code Transaction}\n     */\n    Transaction buildTransaction(final Transaction transaction) {\n        if (attachment.get() != null) {\n\n            final Path path = attachment.get().getFileName();\n\n            if (moveAttachment) {\n                if (moveAttachment() && path != null) {\n                    transaction.setAttachment(path.toString());\n                } else if (path!= null) {\n                    transaction.setAttachment(null);\n\n                    final String message = ResourceUtils.getString(\"Message.Error.TransferAttachment\", path.toString());\n\n                    StaticUIMethods.displayError(message);\n                }\n            }\n        } else {\n            transaction.setAttachment(null);\n        }\n\n        return transaction;\n    }\n\n    private boolean moveAttachment() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        return engine.addAttachment(attachment.get(), false);\n    }\n\n    private void showImageAction() {\n        if (attachment.get() != null) {\n            if (Files.exists(attachment.get())) {\n                ImageDialog.showImage(attachment.get());\n            } else {\n                StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.MissingAttachment\", attachment.get().toString()));\n            }\n        }\n    }\n\n    void clear() {\n        attachment.set(null);\n    }\n\n    private void attachmentAction() {\n        final Preferences pref = Preferences.userNodeForPackage(this.getClass());\n        final String baseFile = EngineFactory.getActiveDatabase();\n\n        final List<String> extensions = new ArrayList<>();\n\n        for (final String suffix : ImageIO.getReaderFileSuffixes()) {\n            extensions.add(\"*.\" + suffix);\n        }\n\n        final FileChooser fileChooser = new FileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n        fileChooser.getExtensionFilters().add(\n                new FileChooser.ExtensionFilter(resources.getString(\"Title.ImageFiles\"), extensions)\n        );\n\n        final String lastDirectory = pref.get(LAST_DIR, null);\n        if (lastDirectory != null) {\n            fileChooser.setInitialDirectory(new File(lastDirectory));\n        }\n\n        if (attachment.get() != null) {\n            final Path path = attachment.get().getFileName();\n            if (path != null) {\n                fileChooser.setInitialFileName(path.toString());\n            }\n        }\n\n        final File selectedFile = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n        if (selectedFile != null) {\n            pref.put(LAST_DIR, selectedFile.getParent());   // save last good directory location\n\n            boolean result = true;\n\n            // TODO, add option to copy the file instead of moving it\n\n            final Path attachmentDirectory = AttachmentUtils.getAttachmentDirectory(Paths.get(baseFile));\n\n            if (baseFile.startsWith(EngineFactory.REMOTE_PREFIX)) { // working remotely\n                moveAttachment = true;\n            } else if (attachmentDirectory != null && !attachmentDirectory.toString().equals(selectedFile.getParent())) {\n                String message = ResourceUtils.getString(\"Message.Warn.MoveFile\", selectedFile.toString(),\n                        attachmentDirectory.toString());\n\n                if (!StaticUIMethods.showConfirmationDialog(resources.getString(\"Title.MoveFile\"), message).getButtonData().isCancelButton()) {\n                    moveAttachment = true;\n\n                    final Path newPath = Paths.get(AttachmentUtils.getAttachmentDirectory(Paths.get(baseFile)) +\n                            FileUtils.SEPARATOR + selectedFile.getName());\n\n                    if (Files.exists(newPath)) {\n                        message = ResourceUtils.getString(\"Message.Warn.SameFile\", selectedFile.toString(),\n                                attachmentDirectory.toString());\n\n                        StaticUIMethods.displayWarning(message);\n                        moveAttachment = false;\n                        result = false;\n                    }\n                } else {\n                    result = false;\n                }\n            }\n\n            if (result) {\n                attachment.set(selectedFile.toPath());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/BankRegisterPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.Objects;\nimport java.util.prefs.Preferences;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\n\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Register pane controller.\n *\n * @author Craig Cavanaugh\n */\npublic class BankRegisterPaneController extends RegisterPaneController {\n\n    private static final String RECENT_TAB = \"/jgnash/uifx/register/recent/tab\";\n\n    @FXML\n    protected Button jumpButton;\n\n    @FXML\n    protected TabPane transactionForms;\n\n    private Tab adjustTab;\n\n    private Tab creditTab;\n\n    private Tab debitTab;\n\n    @FXML\n    @Override\n    public void initialize() {\n\n        super.initialize();\n\n        jumpButton.disableProperty().bind(selectedTransaction.isNull());\n\n        transactionForms.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);\n\n        // Load the register table\n        final RegisterTableController controller = FXMLUtils.loadFXML(o\n                -> registerTablePane.getChildren().add(o), \"BasicRegisterTable.fxml\", resources);\n\n        registerTableController.set(controller);\n\n        accountProperty().addListener((observable, oldValue, newValue) -> buildTabs());\n    }\n\n    @Override\n    protected void modifyTransaction(@NotNull final Transaction transaction) {\n        if (!(transaction instanceof InvestmentTransaction)) {\n            if (transaction.getTransactionType() == TransactionType.SINGLENTRY) {\n                transactionForms.getSelectionModel().select(adjustTab);\n                ((Slip) adjustTab.getUserData()).modifyTransaction(transaction);\n            } else if (transaction.getAmount(accountProperty().get()).signum() >= 0) {\n                transactionForms.getSelectionModel().select(creditTab);\n                ((Slip) creditTab.getUserData()).modifyTransaction(transaction);\n            } else {\n                transactionForms.getSelectionModel().select(debitTab);\n                ((Slip) debitTab.getUserData()).modifyTransaction(transaction);\n            }\n        } else {    // pop a dialog to modify the transaction\n            InvestmentTransactionDialog.show(((InvestmentTransaction) transaction).getInvestmentAccount(), transaction,\n                    newTransaction -> {\n                        if (newTransaction != null) {\n                            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                            Objects.requireNonNull(engine);\n\n                            engine.removeTransaction(transaction);\n                            engine.addTransaction(newTransaction);\n                        }\n                    });\n        }\n    }\n\n    private void buildTabs() {\n        final AccountType accountType = accountProperty().get().getAccountType();\n\n        final String[] tabNames = RegisterFactory.getCreditDebitTabNames(accountType);\n\n        creditTab = buildTab(tabNames[0], SlipType.INCREASE);\n        debitTab = buildTab(tabNames[1], SlipType.DECREASE);\n        adjustTab = buildAdjustTab();\n\n        transactionForms.getTabs().addAll(creditTab, debitTab, buildTransferTab(), adjustTab);\n\n        if (accountType == AccountType.CHECKING || accountType == AccountType.CREDIT) {\n            transactionForms.getSelectionModel().select(debitTab);\n        } else if (accountType.getAccountGroup() == AccountGroup.INCOME) {\n            transactionForms.getSelectionModel().select(debitTab);\n        }\n\n        restoreLastTabUsed();\n\n        transactionForms.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue)\n                -> saveLastTabUsed(newValue.intValue()));\n    }\n\n    private Tab buildTab(final String tabName, final SlipType slipType) {\n        final Tab tab = new Tab(tabName);\n        final SlipController slipController = FXMLUtils.loadFXML(tab::setContent, \"BankSlip.fxml\", resources);\n\n        slipController.setSlipType(slipType);\n        slipController.accountProperty().bind(accountProperty());\n\n        tab.setUserData(slipController); // place a reference to the controller here\n\n        return tab;\n    }\n\n    private Tab buildAdjustTab() {\n        final Tab tab = new Tab(resources.getString(\"Tab.Adjust\"));\n\n        final AdjustmentSlipController adjustmentSlipController = FXMLUtils.loadFXML(tab::setContent,\n                \"AdjustmentSlip.fxml\", resources);\n\n        adjustmentSlipController.accountProperty().bind(accountProperty());\n\n        tab.setUserData(adjustmentSlipController); // place a reference to the controller here\n\n        return tab;\n    }\n\n    private Tab buildTransferTab() {\n        final Tab tab = new Tab(resources.getString(\"Tab.Transfer\"));\n\n        final TransferSlipController slipController = FXMLUtils.loadFXML(tab::setContent,\n                \"TransferSlip.fxml\", resources);\n\n        slipController.accountProperty().bind(accountProperty());\n\n        tab.setUserData(slipController); // place a reference to the controller here\n\n        return tab;\n    }\n\n    private void saveLastTabUsed(final int index) {\n        final Preferences tabPreferences = Preferences.userRoot().node(RECENT_TAB);\n        tabPreferences.putInt(accountProperty().get().getUuid().toString(), index);\n    }\n\n    private void restoreLastTabUsed() {\n        if (Options.restoreLastTabProperty().get()) {\n            Preferences tabPreferences = Preferences.userRoot().node(RECENT_TAB);\n            String id = accountProperty().get().getUuid().toString();\n\n            final int index = tabPreferences.getInt(id, transactionForms.getSelectionModel().getSelectedIndex());\n\n            JavaFXUtils.runLater(() -> transactionForms.getSelectionModel().select(index));\n        }\n    }\n\n    @Override\n    protected void clearForm() {\n        ((Slip) transactionForms.getSelectionModel().getSelectedItem().getUserData()).clearForm();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/BaseSlip.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.beans.property.BooleanProperty;\nimport javafx.scene.Node;\nimport javafx.scene.Parent;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.input.KeyEvent;\n\nimport jgnash.engine.ReconciledState;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Base slip interface.\n *\n * @author Craig Cavanaugh\n */\ninterface BaseSlip {\n\n    void clearForm();\n\n    void handleCancelAction();\n\n    void handleEnterAction();\n\n    /**\n     * Observable value indicating true if the form entry is valid.  Entry acceptance should be bound to this property.\n     *\n     * @return boolean property\n     */\n    BooleanProperty validFormProperty();\n\n    @NotNull\n    CheckBox getReconcileButton();\n\n    default void setReconciledState(final ReconciledState reconciledState) {\n        switch (reconciledState) {\n            case NOT_RECONCILED:\n                getReconcileButton().setIndeterminate(false);\n                getReconcileButton().setSelected(false);\n                break;\n            case RECONCILED:\n                getReconcileButton().setIndeterminate(false);\n                getReconcileButton().setSelected(true);\n                break;\n            case CLEARED:\n                getReconcileButton().setIndeterminate(true);\n        }\n    }\n\n    default ReconciledState getReconciledState() {\n        if (getReconcileButton().isIndeterminate()) {\n            return ReconciledState.CLEARED;\n        } else if (getReconcileButton().isSelected()) {\n            return ReconciledState.RECONCILED;\n        }\n        return ReconciledState.NOT_RECONCILED;\n    }\n\n    /**\n     * Default implementation for intelligent handling for escape and enter keys within a Slip form.\n     * This should be called by the implementing class.\n     *\n     * @param parent Parent node to attach handler to.\n     */\n    default void installKeyPressedHandler(final Parent parent) {\n\n        parent.addEventHandler(KeyEvent.KEY_PRESSED, event -> {\n            if (JavaFXUtils.ESCAPE_KEY.match(event)) {  // clear the form if an escape key is detected\n                clearForm();\n            } else if (JavaFXUtils.ENTER_KEY.match(event)) {    // handle an enter key if detected\n                if (validFormProperty().get()) {\n                    JavaFXUtils.runLater(BaseSlip.this::handleEnterAction);\n                } else {\n                    JavaFXUtils.runLater(() -> {\n                        if (event.getSource() instanceof Node) {\n                            JavaFXUtils.focusNext((Node) event.getSource());\n                        }\n                    });\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/BasicRegisterTableController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\n\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.util.Callback;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.text.NumericFormats;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.time.DateUtils;\n\n/**\n * Register Table with stats controller.\n *\n * @author Craig Cavanaugh\n */\npublic class BasicRegisterTableController extends RegisterTableController {\n\n    @FXML\n    private Label reconciledBalanceLabel;\n\n    private static final double[] PREF_COLUMN_WEIGHTS = {0, 0, 0, 30, 40, 30, 0, 0, 0, 0};\n\n    private static final boolean[] DEFAULT_COLUMN_VISIBILITY = {true, false, true, true, true, true, true, true, true, true};\n\n    @FXML\n    @Override\n    void initialize() {\n        super.initialize();\n\n        // Bind the reconciledBalance Label to the account property wrapper\n        reconciledBalanceLabel.textProperty().bind(getAccountPropertyWrapper().reconciledAmountProperty());\n    }\n\n    @Override\n    Callback<Integer, Double> getColumnWeightFactory() {\n        return param -> PREF_COLUMN_WEIGHTS[param];\n    }\n\n    @Override\n    Callback<Integer, Boolean> getColumnVisibilityFactory() {\n        return param -> DEFAULT_COLUMN_VISIBILITY[param];\n    }\n\n    @Override\n    protected void buildTable() {\n        final String[] columnNames = RegisterFactory.getColumnNames(accountProperty().get().getAccountType());\n\n        final TableColumn<Transaction, LocalDate> dateColumn = new TableColumn<>(columnNames[0]);\n        dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getLocalDate()));\n        dateColumn.setCellFactory(cell -> new TransactionDateTableCell());\n        tableView.getColumns().add(dateColumn);\n\n        final TableColumn<Transaction, LocalDateTime> dateTimeColumn = new TableColumn<>(columnNames[1]);\n        dateTimeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getTimestamp()));\n        dateTimeColumn.setCellFactory(cell -> new TransactionDateTimeTableCell());\n        tableView.getColumns().add(dateTimeColumn);\n\n        final TableColumn<Transaction, String> numberColumn = new TableColumn<>(columnNames[2]);\n        numberColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getNumber()));\n        numberColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(numberColumn);\n\n        final TableColumn<Transaction, String> payeeColumn = new TableColumn<>(columnNames[3]);\n        payeeColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getPayee()));\n        payeeColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(payeeColumn);\n\n        final TableColumn<Transaction, String> memoColumn = new TableColumn<>(columnNames[4]);\n        memoColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getMemo()));\n        memoColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(memoColumn);\n\n        final TableColumn<Transaction, String> accountColumn = new TableColumn<>(columnNames[5]);\n        accountColumn.setCellValueFactory(param -> new AccountNameWrapper(param.getValue()));\n        accountColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(accountColumn);\n\n        final TableColumn<Transaction, String> reconciledColumn = new TableColumn<>(columnNames[6]);\n        reconciledColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue()\n                .getReconciled(account.get()).toString()));\n        reconciledColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(reconciledColumn);\n\n        final Callback<TableColumn<Transaction, BigDecimal>, TableCell<Transaction, BigDecimal>> shortDecimalCellFactory =\n                cell -> new TransactionCommodityFormatTableCell(NumericFormats\n                        .getShortCommodityFormat(account.get().getCurrencyNode()));\n\n        final TableColumn<Transaction, BigDecimal> increaseColumn = new TableColumn<>(columnNames[7]);\n        increaseColumn.setCellValueFactory(param -> new IncreaseAmountProperty(param.getValue()\n                .getAmount(accountProperty().getValue())));\n        increaseColumn.setCellFactory(shortDecimalCellFactory);\n        tableView.getColumns().add(increaseColumn);\n\n        final TableColumn<Transaction, BigDecimal> decreaseColumn = new TableColumn<>(columnNames[8]);\n        decreaseColumn.setCellValueFactory(param -> new DecreaseAmountProperty(param.getValue()\n                .getAmount(accountProperty().getValue())));\n        decreaseColumn.setCellFactory(shortDecimalCellFactory);\n        tableView.getColumns().add(decreaseColumn);\n\n        final TableColumn<Transaction, BigDecimal> balanceColumn = new TableColumn<>(columnNames[9]);\n        balanceColumn.setCellValueFactory(param -> {\n            final AccountType accountType = accountProperty().getValue().getAccountType();\n\n            return new SimpleObjectProperty<>(AccountBalanceDisplayManager.\n                    convertToSelectedBalanceMode(accountType, getBalanceAt(param.getValue())));\n        });\n\n        balanceColumn.setCellFactory(cell -> new TransactionCommodityFormatTableCell(NumericFormats\n                .getFullCommodityFormat(account.get().getCurrencyNode())));\n        balanceColumn.setSortable(false);   // do not allow a sort on the balance\n        tableView.getColumns().add(balanceColumn);\n\n        tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        tableViewManager.setColumnFormatFactory(param -> {\n            if (param == balanceColumn) {\n                return NumericFormats.getFullCommodityFormat(accountProperty().getValue().getCurrencyNode());\n            } else if (param == increaseColumn || param == decreaseColumn) {\n                return NumericFormats.getShortCommodityFormat(accountProperty().getValue().getCurrencyNode());\n            } else if (param == dateColumn) {\n                return DateUtils.getShortDateFormatter().toFormat();\n            } else if (param == dateTimeColumn) {\n                return DateUtils.getShortDateTimeFormatter().toFormat();\n            }\n\n            return null;\n        });\n    }\n\n    private BigDecimal getBalanceAt(final Transaction transaction) {\n        BigDecimal balance = BigDecimal.ZERO;\n\n        final Account account = this.account.get();\n\n        if (account != null) {\n            final int index = sortedList.indexOf(transaction);\n\n            for (int i = 0; i <= index; i++) {\n                balance = balance.add(sortedList.get(i).getAmount(account));\n            }\n        }\n        return balance;\n    }\n\n    private class AccountNameWrapper extends SimpleStringProperty {\n        final String split = resources.getString(\"Button.Splits\");\n\n        AccountNameWrapper(final Transaction t) {\n            super();\n\n            if (t instanceof InvestmentTransaction) {\n                setValue(((InvestmentTransaction) t).getInvestmentAccount().getName());\n            } else {\n                int count = t.size();\n                if (count > 1) {\n                    setValue(\"[ \" + count + \" \" + split + \" ]\");\n                } else {\n                    Account creditAccount = t.getTransactionEntries().get(0).getCreditAccount();\n                    if (creditAccount != account.get()) {\n                        setValue(creditAccount.getName());\n                    } else {\n                        setValue(t.getTransactionEntries().get(0).getDebitAccount().getName());\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/BuyShareSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.AbstractInvestmentTransactionEntry;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionEntryBuyX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Credits and Debits.\n *\n * @author Craig Cavanaugh\n */\npublic class BuyShareSlipController extends AbstractPriceQtyInvSlipController {\n\n    @FXML\n    private FeePane feePane;\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    @FXML\n    private AccountExchangePane accountExchangePane;\n\n    @FXML\n    public void initialize() {\n        super.initialize();\n\n        // don't filter the base account for investment transactions\n        accountExchangePane.filterBaseAccountProperty().set(false);\n\n        // Bind necessary properties to the exchange panel\n        accountExchangePane.baseAccountProperty().bind(accountProperty());\n        accountExchangePane.amountProperty().bindBidirectional(totalField.decimalProperty());\n        accountExchangePane.amountEditableProperty().bind(totalField.editableProperty());\n\n        feePane.accountProperty().bind(accountProperty());\n\n        accountProperty().addListener((observable, oldValue, newValue) -> clearForm());\n\n        final ChangeListener<BigDecimal> changeListener = (observable, oldValue, newValue) -> updateTotalField();\n\n        quantityField.decimalProperty().addListener(changeListener);\n        priceField.decimalProperty().addListener(changeListener);\n        feePane.decimalProperty().addListener(changeListener);\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (transaction.getTransactionType() != TransactionType.BUYSHARE || !(transaction instanceof InvestmentTransaction)) {\n            throw new IllegalArgumentException(resources.getString(\"Message.Error.InvalidTransactionType\"));\n        }\n        clearForm();\n\n        datePicker.setValue(transaction.getLocalDate());\n        numberComboBox.setValue(transaction.getNumber());\n\n        feePane.setTransactionEntries(((InvestmentTransaction) transaction).getInvestmentFeeEntries());\n\n        transaction.getTransactionEntries().stream().filter(TransactionEntryBuyX.class::isInstance).forEach(e -> {\n            final AbstractInvestmentTransactionEntry entry = (AbstractInvestmentTransactionEntry) e;\n\n            memoTextField.setText(e.getMemo());\n            priceField.setDecimal(entry.getPrice());\n            quantityField.setDecimal(entry.getQuantity());\n            securityComboBox.setSecurityNode(entry.getSecurityNode());\n\n            /* TODO by default investment account is assigned to debit account.  Should only have to look at the\n             * credit side of the entry for information\n             */\n            if (entry.getCreditAccount().equals(accountProperty().get())) {\n                accountExchangePane.setSelectedAccount(entry.getDebitAccount());\n                accountExchangePane.setExchangedAmount(entry.getDebitAmount().abs());\n            } else {\n                accountExchangePane.setSelectedAccount(entry.getCreditAccount());\n                accountExchangePane.setExchangedAmount(entry.getCreditAmount());\n            }\n        });\n\n        tagPane.setSelectedTags(transaction.getTags(TransactionEntryBuyX.class));\n\n        modTrans = transaction;\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        setReconciledState(transaction.getReconciled(accountProperty().get()));\n    }\n\n    private void updateTotalField() {\n        totalField.setDecimal(quantityField.getDecimal().multiply(priceField.getDecimal()).add(feePane.getDecimal()));\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        final BigDecimal exchangeRate = accountExchangePane.getExchangeRate();\n\n        final List<TransactionEntry> fees = feePane.getTransactions();\n\n        final Transaction transaction =  TransactionFactory.generateBuyXTransaction(accountExchangePane.getSelectedAccount(),\n                accountProperty().get(), securityComboBox.getValue(), priceField.getDecimal(),\n                quantityField.getDecimal(), exchangeRate, datePicker.getValue(), memoTextField.getText(), fees);\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(TransactionEntryBuyX.class, tagPane.getSelectedTags());\n\n        return attachmentPane.buildTransaction(transaction);\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        feePane.clearForm();\n\n        attachmentPane.clear();\n        accountExchangePane.setExchangedAmount(null);\n        accountExchangePane.setSelectedAccount(accountProperty().get());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/DateTransNumberDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.fxml.FXML;\nimport javafx.stage.Stage;\nimport jgnash.engine.Account;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.TransactionNumberComboBox;\nimport jgnash.util.NotNull;\n\nimport java.time.LocalDate;\n\n/**\n * A controller for getting a date and transaction number.\n *\n * @author Craig Cavanaugh\n */\npublic class DateTransNumberDialogController {\n\n    @FXML\n    private DatePickerEx dateField;\n\n    @FXML\n    private TransactionNumberComboBox transactionNumberField;\n\n    private boolean result;\n\n    public void setAccount(@NotNull Account account) {\n        transactionNumberField.accountProperty().setValue(account);\n    }\n\n    /**\n     * Returns true or false depending on if the user closed the dialog with\n     * the OK button or Cancel button.\n     *\n     * @return the closing state of the dialog\n     */\n    public boolean getResult() {\n        return result;\n    }\n\n    public LocalDate getDate() {\n        return dateField.getValue();\n    }\n\n    public String getNumber() {\n        return transactionNumberField.getValue();\n    }\n\n    @FXML\n    private void okAction() {\n        result = true;\n        ((Stage) dateField.getScene().getWindow()).close();\n    }\n\n    @FXML\n    private void cancelAction() {\n        ((Stage) dateField.getScene().getWindow()).close();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/DecreaseAmountProperty.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\n\nimport javafx.beans.property.SimpleObjectProperty;\n\n/**\n * UI helper.  A negative BigDecimal is shown as positive or not shown at all.\n *\n* @author Craig Cavanaugh\n*/\nclass DecreaseAmountProperty extends SimpleObjectProperty<BigDecimal> {\n    DecreaseAmountProperty(final BigDecimal value) {\n        if (value.signum() < 0) {\n            setValue(value.abs());\n        } else {\n            setValue(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/DividendSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\n\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntryDividendX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Dividend entry controller.\n *\n * @author Craig Cavanaugh\n */\npublic class DividendSlipController extends AbstractInvIncomeSlipController {\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        BigDecimal incomeExchangedAmount = decimalTextField.getDecimal().negate();\n\n        BigDecimal accountExchangedAmount = decimalTextField.getDecimal();\n\n        if (!incomeExchangePane.getSelectedAccount().getCurrencyNode().equals(accountProperty().get().getCurrencyNode())) {\n            incomeExchangedAmount = incomeExchangePane.exchangeAmountProperty().get().negate();\n        }\n\n        if (!accountExchangePane.getSelectedAccount().getCurrencyNode().equals(accountProperty().get().getCurrencyNode())) {\n            accountExchangedAmount = accountExchangePane.exchangeAmountProperty().get();\n        }\n\n        final Transaction transaction = TransactionFactory.generateDividendXTransaction(incomeExchangePane.getSelectedAccount(),\n                accountProperty().get(), accountExchangePane.getSelectedAccount(), securityComboBox.getValue(),\n                decimalTextField.getDecimal(), incomeExchangedAmount, accountExchangedAmount, datePicker.getValue(),\n                memoTextField.getText());\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(TransactionEntryDividendX.class, tagPane.getSelectedTags());\n\n        return transaction;\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull Transaction transaction) {\n        super.modifyTransaction(transaction);\n\n        tagPane.setSelectedTags(transaction.getTags(TransactionEntryDividendX.class));\n    }\n\n    @Override\n    TransactionType getTransactionType() {\n        return TransactionType.DIVIDEND;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/FeeDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.layout.StackPane;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\nimport java.util.ResourceBundle;\n\n/**\n * Fees entry dialog.\n *\n * @author Craig Cavanaugh\n */\nclass FeeDialog extends AbstractTransactionEntryDialog {\n\n    private static final String PREF_NODE_USER_ROOT = \"/jgnash/uifx/views/register/fees\";\n\n    @FXML\n    private StackPane formPane;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private\n    FeeTransactionEntrySlipController feeController;\n\n    FeeDialog() {\n        FXMLUtils.loadFXML(this, \"FeeDialog.fxml\", ResourceUtils.getBundle());\n        setTitle(ResourceUtils.getString(\"Title.InvFees\"));\n    }\n\n    @Override\n    String getPrefNode() {\n        return PREF_NODE_USER_ROOT;\n    }\n\n    @Override\n    void newAction() {\n        feeController.clearForm();\n        tableView.getSelectionModel().clearSelection();\n    }\n\n    @Override\n    void deleteAction() {\n        final TransactionEntry entry = tableView.getSelectionModel().getSelectedItem();\n        if (entry != null) {\n            tableView.getSelectionModel().clearSelection();\n            feeController.clearForm();\n            getTransactionEntries().remove(entry);\n        }\n    }\n\n    @Override\n    void modifyTransactionEntry(@NotNull final TransactionEntry transactionEntry) {\n        feeController.modifyTransactionEntry(transactionEntry);\n    }\n\n    @Override\n   void initForm() {\n        feeController = FXMLUtils.loadFXML(o -> formPane.getChildren().addAll(o),\n                \"FeeTransactionEntrySlip.fxml\", resources);\n\n        feeController.accountProperty().bind(accountProperty());\n        feeController.transactionEntryListProperty().setValue(getTransactionEntries());\n        feeController.comparatorProperty().bind(tableView.comparatorProperty());\n\n        feeController.setSlipType(SlipType.DECREASE);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/FeePane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.SimpleObjectProperty;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionTag;\nimport jgnash.uifx.control.DetailedDecimalTextField;\n\n/**\n * UI Panel for handling investment transaction fees\n * <p>\n * If {@code feeList.size() > 0 }, then a one or more specialized fees exist.  Otherwise, the fee is\n * simple and charged against the account adjusting the cash balance.\n *\n * @author Craig Cavanaugh\n */\npublic class FeePane extends DetailedDecimalTextField {\n\n    private final SimpleObjectProperty<Account> account = new SimpleObjectProperty<>(null);\n\n    private FeeDialog feeDialog;\n\n    public FeePane() {\n        initialize();\n    }\n\n    private void initialize() {\n        feeDialog = new FeeDialog();\n\n        account.addListener((observable, oldValue, newValue) ->\n                feeDialog.accountProperty().set(accountProperty().get()));\n    }\n\n    @Override\n    public void show() {\n        feeDialog.show(() -> {\n            editableProperty().setValue(feeDialog.getTransactionEntries().size() == 0);\n\n            if (feeDialog.getTransactionEntries().size() != 0) {\n                setDecimal(feeDialog.getBalance().abs());\n            }\n        });\n    }\n\n    public List<TransactionEntry> getTransactions() {\n\n        final List<TransactionEntry> feeList = feeDialog.getTransactionEntries();\n\n        // adjust the cash balance of the investment account\n        if (feeList.isEmpty() && getDecimal().compareTo(BigDecimal.ZERO) != 0) {  // ignore zero balance fees\n            TransactionEntry fee = new TransactionEntry(accountProperty().get(), getDecimal().abs().negate());\n            fee.setTransactionTag(TransactionTag.INVESTMENT_FEE);\n\n            feeList.add(fee);\n        }\n        return feeList;\n    }\n\n    /**\n     * Clones a {@code List} of {@code TransactionEntry(s)}.\n     *\n     * @param fees {@code List} of fees to clone\n     */\n    void setTransactionEntries(final List<TransactionEntry> fees) {\n        final List<TransactionEntry> feeList = feeDialog.getTransactionEntries();\n\n        if (fees.size() == 1) {\n            TransactionEntry e = fees.get(0);\n\n            if (e.getCreditAccount().equals(e.getDebitAccount())) {\n                setDecimal(e.getAmount(accountProperty().get()).abs());\n            } else {\n                try {\n                    feeList.add((TransactionEntry) e.clone()); // copy over the provided set's entry\n                } catch (CloneNotSupportedException e1) {\n                    Logger.getLogger(FeePane.class.getName()).log(Level.SEVERE, e1.getLocalizedMessage(), e1);\n                }\n                setDecimal(sumFees().abs());\n            }\n        } else {\n            for (final TransactionEntry entry : fees) { // clone the provided set's entries\n                try {\n                    feeList.add((TransactionEntry) entry.clone());\n                } catch (CloneNotSupportedException e) {\n                    Logger.getLogger(FeePane.class.getName()).log(Level.SEVERE, e.toString(), e);\n                }\n            }\n\n            setDecimal(sumFees().abs());\n        }\n\n        editableProperty().setValue(feeList.isEmpty());\n    }\n\n    private BigDecimal sumFees() {\n        BigDecimal sum = BigDecimal.ZERO;\n\n        for (final TransactionEntry entry : feeDialog.getTransactionEntries()) {\n            sum = sum.add(entry.getAmount(accountProperty().get()));\n        }\n\n        return sum;\n    }\n\n    /**\n     * Clear the form and remove all entries.\n     */\n    void clearForm() {\n        feeDialog.getTransactionEntries().clear();\n        setDecimal(BigDecimal.ZERO);\n    }\n\n    public SimpleObjectProperty<Account> accountProperty() {\n        return account;\n    }\n}\n\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/FeeTransactionEntrySlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionTag;\n\n/**\n * Fees Entry Controller for investment fees.\n *\n * @author Craig Cavanaugh\n */\npublic class FeeTransactionEntrySlipController extends SplitTransactionSlipController {\n\n    @Override\n    TransactionEntry buildTransactionEntry() {\n        final TransactionEntry entry = super.buildTransactionEntry();\n        entry.setTransactionTag(TransactionTag.INVESTMENT_FEE);\n\n        return entry;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/GainLossDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.layout.StackPane;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\nimport java.util.ResourceBundle;\n\n/**\n * Gains / Loss entry dialog.\n *\n * @author Craig Cavanaugh\n */\nclass GainLossDialog extends AbstractTransactionEntryDialog {\n\n    private static final String PREF_NODE_USER_ROOT = \"/jgnash/uifx/views/register/gainLoss\";\n\n    @FXML\n    private StackPane formPane;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private\n    GainLossTransactionEntrySlipController gainLossController;\n\n    GainLossDialog() {\n        FXMLUtils.loadFXML(this, \"GainLossDialog.fxml\", ResourceUtils.getBundle());\n        setTitle(ResourceUtils.getString(\"Title.InvGainsLoss\"));\n    }\n\n    @Override\n    String[] getSplitColumnName() {\n        return RegisterFactory.getGainLossSplitColumnName();\n    }\n\n    @Override\n    String getPrefNode() {\n        return PREF_NODE_USER_ROOT;\n    }\n\n    @Override\n    void newAction() {\n        gainLossController.clearForm();\n        tableView.getSelectionModel().clearSelection();\n    }\n\n    @Override\n    void deleteAction() {\n        final TransactionEntry entry = tableView.getSelectionModel().getSelectedItem();\n        if (entry != null) {\n            tableView.getSelectionModel().clearSelection();\n            gainLossController.clearForm();\n            getTransactionEntries().remove(entry);\n        }\n    }\n\n    @Override\n    void modifyTransactionEntry(@NotNull final TransactionEntry transactionEntry) {\n        gainLossController.modifyTransactionEntry(transactionEntry);\n    }\n\n    @Override\n   void initForm() {\n        gainLossController = FXMLUtils.loadFXML(o -> formPane.getChildren().addAll(o),\n                \"GainLossTransactionEntrySlip.fxml\", resources);\n\n        gainLossController.accountProperty().bind(accountProperty());\n        gainLossController.transactionEntryListProperty().set(getTransactionEntries());\n        gainLossController.comparatorProperty().bind(tableView.comparatorProperty());\n\n        gainLossController.setSlipType(SlipType.INCREASE);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/GainLossPane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.SimpleObjectProperty;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionTag;\nimport jgnash.uifx.control.DetailedDecimalTextField;\n\n/**\n * UI Panel for handling investment transaction fees\n * <p>\n * If {@code feeList.size() > 0 }, then a one or more specialized fees exist.  Otherwise, the fee is\n * simple and charged against the account adjusting the cash balance.\n *\n * @author Craig Cavanaugh\n */\npublic class GainLossPane extends DetailedDecimalTextField {\n\n    private final SimpleObjectProperty<Account> account = new SimpleObjectProperty<>(null);\n\n    private GainLossDialog gainLossDialog;\n\n    public GainLossPane() {\n        initialize();\n    }\n\n    private void initialize() {\n        gainLossDialog = new GainLossDialog();\n\n        account.addListener((observable, oldValue, newValue)\n                -> gainLossDialog.accountProperty().set(accountProperty().get()));\n    }\n\n    @Override\n    public void show() {\n        gainLossDialog.show(() -> {\n            editableProperty().setValue(gainLossDialog.getTransactionEntries().size() == 0);\n\n            if (gainLossDialog.getTransactionEntries().size() != 0) {\n                setDecimal(gainLossDialog.getBalance().abs());\n            }\n        });\n    }\n\n    public List<TransactionEntry> getTransactions() {\n\n        final List<TransactionEntry> feeList = gainLossDialog.getTransactionEntries();\n\n        // adjust the cash balance of the investment account\n        if (feeList.isEmpty() && getDecimal().compareTo(BigDecimal.ZERO) != 0) {  // ignore zero balance fees\n            TransactionEntry fee = new TransactionEntry(accountProperty().get(), getDecimal().abs().negate());\n            fee.setTransactionTag(TransactionTag.GAIN_LOSS);\n\n            feeList.add(fee);\n        }\n        return feeList;\n    }\n\n    /**\n     * Clones a {@code List} of {@code TransactionEntry(s)}.\n     *\n     * @param fees {@code List} of fees to clone\n     */\n    void setTransactionEntries(final List<TransactionEntry> fees) {\n        final List<TransactionEntry> transactionEntries = gainLossDialog.getTransactionEntries();\n\n        if (fees.size() == 1) {\n            TransactionEntry e = fees.get(0);\n\n            if (e.getCreditAccount().equals(e.getDebitAccount())) {\n                setDecimal(e.getAmount(accountProperty().get()).abs());\n            } else {\n                try {\n                    transactionEntries.add((TransactionEntry) e.clone()); // copy over the provided set's entry\n                } catch (CloneNotSupportedException e1) {\n                    Logger.getLogger(GainLossPane.class.getName()).log(Level.SEVERE, e1.getLocalizedMessage(), e1);\n                }\n                setDecimal(sumGains().abs());\n            }\n        } else {\n            for (final TransactionEntry entry : fees) { // clone the provided set's entries\n                try {\n                    transactionEntries.add((TransactionEntry) entry.clone());\n                } catch (CloneNotSupportedException e) {\n                    Logger.getLogger(GainLossPane.class.getName()).log(Level.SEVERE, e.toString(), e);\n                }\n            }\n\n            setDecimal(sumGains().abs());\n        }\n\n        editableProperty().setValue(transactionEntries.isEmpty());\n    }\n\n    private BigDecimal sumGains() {\n        BigDecimal sum = BigDecimal.ZERO;\n\n        for (TransactionEntry entry : gainLossDialog.getTransactionEntries()) {\n            sum = sum.add(entry.getAmount(accountProperty().get()));\n        }\n\n        return sum;\n    }\n\n    /**\n     * Clear the form and remove all entries.\n     */\n    void clearForm() {\n        gainLossDialog.getTransactionEntries().clear();\n        setDecimal(BigDecimal.ZERO);\n        editableProperty().setValue(true);\n    }\n\n    public SimpleObjectProperty<Account> accountProperty() {\n        return account;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/GainLossTransactionEntrySlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionTag;\n\n\n/**\n * Split Transaction Entry Controller for investment gains and loss.\n *\n * @author Craig Cavanaugh\n */\npublic class GainLossTransactionEntrySlipController extends SplitTransactionSlipController {\n\n    @Override\n    TransactionEntry buildTransactionEntry() {\n        final TransactionEntry entry = super.buildTransactionEntry();\n        entry.setTransactionTag(TransactionTag.GAIN_LOSS);\n\n        return entry;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/IncreaseAmountProperty.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\n\nimport javafx.beans.property.SimpleObjectProperty;\n\n/**\n * UI helper. If a BigDecimal is negative, do not show it.\n *\n * @author Craig Cavanaugh\n */\nclass IncreaseAmountProperty extends SimpleObjectProperty<BigDecimal> {\n    IncreaseAmountProperty(final BigDecimal value) {\n        if (value.signum() >= 0) {\n            setValue(value);\n        } else {\n            setValue(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/InvestmentRegisterPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.Objects;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.layout.StackPane;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.views.accounts.SelectAccountSecuritiesDialog;\nimport jgnash.util.NotNull;\n\n/**\n * Investment Register pane controller.\n *\n * @author Craig Cavanaugh\n */\npublic class InvestmentRegisterPaneController extends RegisterPaneController {\n\n    @FXML\n    private Button enterButton;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private ComboBox<SlipControllerContainer> actionComboBox;\n\n    @FXML\n    private StackPane transactionSlips;\n\n    private InvestmentSlipManager investmentSlipManager;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        // Load the register table\n        registerTableController.set(FXMLUtils.loadFXML(node ->\n                registerTablePane.getChildren().add(node), \"InvestmentRegisterTable.fxml\", resources));\n\n        investmentSlipManager = new InvestmentSlipManager(transactionSlips, actionComboBox);\n        investmentSlipManager.accountProperty().bind(accountProperty());\n\n        actionComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                enterButton.disableProperty().bind(newValue.getController().validFormProperty().not());\n            }\n        });\n    }\n\n    @FXML\n    private void handleEnterAction() {\n        actionComboBox.getSelectionModel().selectedItemProperty().get().getController().handleEnterAction();\n    }\n\n    @FXML\n    private void handleCancelAction() {\n        actionComboBox.getSelectionModel().selectedItemProperty().get().getController().handleCancelAction();\n    }\n\n    @FXML\n    private void handleSecuritiesAction() {\n        final SelectAccountSecuritiesDialog dialog = new SelectAccountSecuritiesDialog(accountProperty().get(),\n                accountProperty().get().getSecurities());\n\n        if (dialog.showAndWait()) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.updateAccountSecurities(accountProperty().get(), dialog.getSelectedSecurities());\n        }\n    }\n\n    @Override\n    protected void modifyTransaction(@NotNull final Transaction transaction) {\n        if (transaction.areAccountsLocked()) {\n            StaticUIMethods.displayError(resources.getString(\"Message.TransactionModifyLocked\"));\n            return;\n        }\n\n        investmentSlipManager.modifyTransaction(transaction);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/InvestmentRegisterTableController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.Format;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\n\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TableColumn;\nimport javafx.util.Callback;\n\nimport jgnash.engine.CommodityNode;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.Transaction;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\n\n/**\n * Investment Register Table with stats controller.\n *\n * @author Craig Cavanaugh\n */\npublic class InvestmentRegisterTableController extends RegisterTableController {\n\n    @FXML\n    private Label cashBalanceLabel;\n\n    @FXML\n    private Label marketValueLabel;\n\n    final private double[] PREF_COLUMN_WEIGHTS = {0, 0, 33, 33, 34, 0, 0, 0, 0};\n\n    private static final boolean[] DEFAULT_COLUMN_VISIBILITY = {true, false, true, true, true, true, true, true, true};\n\n    private NumberFormat quantityNumberFormat;\n\n    @FXML\n    @Override\n    void initialize() {\n        super.initialize();\n\n        // Bind the label text to the account property wrapper\n        cashBalanceLabel.textProperty().bind(getAccountPropertyWrapper().cashBalanceProperty());\n        marketValueLabel.textProperty().bind(getAccountPropertyWrapper().marketValueProperty());\n    }\n\n    @Override\n    Callback<Integer, Double> getColumnWeightFactory() {\n        return param -> PREF_COLUMN_WEIGHTS[param];\n    }\n\n    @Override\n    Callback<Integer, Boolean> getColumnVisibilityFactory() {\n        return param -> DEFAULT_COLUMN_VISIBILITY[param];\n    }\n\n    @Override\n    protected void buildTable() {\n        final String[] columnNames = RegisterFactory.getColumnNames(accountProperty().get().getAccountType());\n\n        final TableColumn<Transaction, LocalDate> dateColumn = new TableColumn<>(columnNames[0]);\n        dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getLocalDate()));\n        dateColumn.setCellFactory(cell -> new TransactionDateTableCell());\n        tableView.getColumns().add(dateColumn);\n\n        final TableColumn<Transaction, LocalDateTime> dateTimeColumn = new TableColumn<>(columnNames[1]);\n        dateTimeColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getTimestamp()));\n        dateTimeColumn.setCellFactory(cell -> new TransactionDateTimeTableCell());\n        tableView.getColumns().add(dateTimeColumn);\n\n        final TableColumn<Transaction, String> typeColumn = new TableColumn<>(columnNames[2]);\n        typeColumn.setCellValueFactory(param -> new TransactionTypeWrapper(param.getValue()));\n        typeColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(typeColumn);\n\n        final TableColumn<Transaction, String> investmentColumn = new TableColumn<>(columnNames[3]);\n        investmentColumn.setCellValueFactory(param -> new TransactionSymbolWrapper(param.getValue()));\n        investmentColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(investmentColumn);\n\n        final TableColumn<Transaction, String> memoColumn = new TableColumn<>(columnNames[4]);\n        memoColumn.setCellValueFactory(param -> new MemoWrapper(param.getValue()));\n        memoColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(memoColumn);\n\n        final TableColumn<Transaction, String> reconciledColumn = new TableColumn<>(columnNames[5]);\n        reconciledColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue()\n                .getReconciled(account.getValue()).toString()));\n        reconciledColumn.setCellFactory(cell -> new TransactionStringTableCell());\n        tableView.getColumns().add(reconciledColumn);\n\n        final TableColumn<Transaction, BigDecimal> quantityColumn = new TableColumn<>(columnNames[6]);\n        quantityColumn.setCellValueFactory(param -> new QuantityProperty(param.getValue()));\n        quantityColumn.setCellFactory(cell -> new InvestmentTransactionQuantityTableCell());\n        tableView.getColumns().add(quantityColumn);\n\n        final TableColumn<Transaction, BigDecimal> priceColumn = new TableColumn<>(columnNames[7]);\n        priceColumn.setCellValueFactory(param -> new PriceProperty(param.getValue()));\n        priceColumn.setCellFactory(cell\n                -> new TransactionCommodityFormatTableCell(NumericFormats.getShortCommodityFormat(account.get().getCurrencyNode())));\n        tableView.getColumns().add(priceColumn);\n\n        final TableColumn<Transaction, BigDecimal> netColumn = new TableColumn<>(columnNames[8]);\n        netColumn.setCellValueFactory(param -> new AmountProperty(param.getValue()));\n        netColumn.setCellFactory(cell\n                -> new TransactionCommodityFormatTableCell(NumericFormats.getFullCommodityFormat(account.get().getCurrencyNode())));\n        tableView.getColumns().add(netColumn);\n\n        tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        tableViewManager.setColumnFormatFactory(param -> {\n            if (param == netColumn) {\n                return NumericFormats.getFullCommodityFormat(accountProperty().getValue().getCurrencyNode());\n            } else if (param == quantityColumn) {\n                return getQuantityColumnFormat();\n            } else if (param == priceColumn) {\n                return NumericFormats.getShortCommodityFormat(accountProperty().getValue().getCurrencyNode());\n            } else if (param == dateColumn) {\n                return DateUtils.getShortDateFormatter().toFormat();\n            } else if (param == dateTimeColumn) {\n                return DateUtils.getShortDateTimeFormatter().toFormat();\n            }\n\n            return null;\n        });\n    }\n\n    private Format getQuantityColumnFormat() {\n        if (quantityNumberFormat == null) {\n            if (account.get() != null) {\n                final int max = account.get().getUsedSecurities().parallelStream().mapToInt(CommodityNode::getScale)\n                        .max().orElse(MathConstants.DEFAULT_COMMODITY_PRECISION);\n\n                quantityNumberFormat = NumericFormats.getFixedPrecisionFormat(max);\n            }\n\n            // Just return a default for now\n            return NumericFormats.getFixedPrecisionFormat(MathConstants.DEFAULT_COMMODITY_PRECISION);\n        }\n        return quantityNumberFormat;\n    }\n\n    private class TransactionTypeWrapper extends SimpleStringProperty {\n        TransactionTypeWrapper(final Transaction t) {\n            super();\n\n            if (t instanceof InvestmentTransaction) {\n                setValue(t.getTransactionType().toString());\n            } else if (t.getAmount(account.get()).signum() > 0) {\n                setValue(resources.getString(\"Item.CashDeposit\"));\n            } else {\n                setValue(resources.getString(\"Item.CashWithdrawal\"));\n            }\n        }\n    }\n\n    private static class TransactionSymbolWrapper extends SimpleStringProperty {\n        TransactionSymbolWrapper(final Transaction t) {\n            super();\n\n            if (t instanceof InvestmentTransaction) {\n                setValue(((InvestmentTransaction) t).getSecurityNode().getSymbol());\n            } else {\n                setValue(null);\n            }\n        }\n    }\n\n    private static class MemoWrapper extends SimpleStringProperty {\n        MemoWrapper(final Transaction t) {\n            super();\n            setValue(t.getMemo());\n        }\n    }\n\n    private static class QuantityProperty extends SimpleObjectProperty<BigDecimal> {\n        QuantityProperty(final Transaction t) {\n            if (t instanceof InvestmentTransaction) {\n                final BigDecimal quantity = ((InvestmentTransaction) t).getQuantity();\n\n                if (quantity.compareTo(BigDecimal.ZERO) != 0) {\n                    setValue(quantity);\n                }\n            } else {\n                setValue(null);\n            }\n        }\n    }\n\n    private static class PriceProperty extends SimpleObjectProperty<BigDecimal> {\n        PriceProperty(final Transaction t) {\n            if (t instanceof InvestmentTransaction) {\n                final BigDecimal price = ((InvestmentTransaction) t).getPrice();\n\n                if (price.compareTo(BigDecimal.ZERO) != 0) {\n                    setValue(price);\n                }\n            } else {\n                setValue(null);\n            }\n        }\n    }\n\n    private class AmountProperty extends SimpleObjectProperty<BigDecimal> {\n        AmountProperty(final Transaction t) {\n            if (t instanceof InvestmentTransaction) {\n                setValue(((InvestmentTransaction) t).getNetCashValue());\n            } else {\n                setValue(t.getAmount(account.get()));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/InvestmentSlipManager.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.layout.StackPane;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Manager for investment transaction forms.\n */\nclass InvestmentSlipManager {\n    private final ComboBox<SlipControllerContainer> actionComboBox;\n\n    private final StackPane transactionSlips;\n\n    private final ObjectProperty<Account> accountProperty = new SimpleObjectProperty<>();\n\n    private final ResourceBundle resources = ResourceUtils.getBundle();\n\n    InvestmentSlipManager(final StackPane transactionSlips, final ComboBox<SlipControllerContainer> actionComboBox) {\n        this.transactionSlips = transactionSlips;\n        this.actionComboBox = actionComboBox;\n\n        initialize();\n    }\n\n    private void initialize() {\n        actionComboBox.setEditable(false);\n\n        actionComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {\n            if (oldValue != null) {\n                oldValue.getController().clearForm();\n            }\n            transactionSlips.getChildren().clear();\n            transactionSlips.getChildren().addAll(newValue.getPane());\n        });\n\n        accountProperty().addListener((observable, oldValue, newValue) -> buildTabs());\n    }\n\n    ObjectProperty<Account> accountProperty() {\n        return accountProperty;\n    }\n\n    private void buildTabs() {\n        final String[] actions = new String[]{resources.getString(\"Transaction.BuyShare\"),\n                resources.getString(\"Transaction.SellShare\"), resources.getString(\"Transaction.TransferIn\"),\n                resources.getString(\"Transaction.TransferOut\"), resources.getString(\"Transaction.AddShare\"),\n                resources.getString(\"Transaction.RemoveShare\"), resources.getString(\"Transaction.ReinvestDiv\"),\n                resources.getString(\"Transaction.Dividend\"), resources.getString(\"Transaction.SplitShare\"),\n                resources.getString(\"Transaction.MergeShare\"), resources.getString(\"Transaction.ReturnOfCapital\")};\n\n        final List<SlipControllerContainer> transactionPanes = new ArrayList<>();\n\n        transactionPanes.add(buildBuyShareTab(actions[0]));\n        transactionPanes.add(buildSellShareTab(actions[1]));\n        transactionPanes.add(buildCashTransferTab(actions[2], SlipType.INCREASE));\n        transactionPanes.add(buildCashTransferTab(actions[3], SlipType.DECREASE));\n        transactionPanes.add(buildAdjustShareTab(actions[4], TransactionType.ADDSHARE));\n        transactionPanes.add(buildAdjustShareTab(actions[5], TransactionType.REMOVESHARE));\n        transactionPanes.add(buildReinvestDividendTab(actions[6]));\n        transactionPanes.add(buildDividendTab(actions[7]));\n        transactionPanes.add(buildSplitMergeTab(actions[8], TransactionType.SPLITSHARE));\n        transactionPanes.add(buildSplitMergeTab(actions[9], TransactionType.MERGESHARE));\n        transactionPanes.add(buildReturnOfCapitalTab(actions[10]));\n\n        actionComboBox.getItems().addAll(transactionPanes);\n\n        actionComboBox.getSelectionModel().select(0);    // force default selection\n    }\n\n    void modifyTransaction(@NotNull final Transaction transaction) {\n        if (transaction.areAccountsLocked()) {\n            StaticUIMethods.displayError(resources.getString(\"Message.TransactionModifyLocked\"));\n            return;\n        }\n\n        if (transaction instanceof InvestmentTransaction) {\n            switch (transaction.getTransactionType()) {\n                case BUYSHARE:\n                    actionComboBox.getSelectionModel().select(0);\n                    break;\n                case SELLSHARE:\n                    actionComboBox.getSelectionModel().select(1);\n                    break;\n                case ADDSHARE:\n                    actionComboBox.getSelectionModel().select(4);\n                    break;\n                case REMOVESHARE:\n                    actionComboBox.getSelectionModel().select(5);\n                    break;\n                case REINVESTDIV:\n                    actionComboBox.getSelectionModel().select(6);\n                    break;\n                case DIVIDEND:\n                    actionComboBox.getSelectionModel().select(7);\n                    break;\n                case SPLITSHARE:\n                    actionComboBox.getSelectionModel().select(8);\n                    break;\n                case MERGESHARE:\n                    actionComboBox.getSelectionModel().select(9);\n                    break;\n                case RETURNOFCAPITAL:\n                    actionComboBox.getSelectionModel().select(10);\n                    break;\n                default:\n                    break;\n            }\n        } else {\n            if (transaction.getAmount(accountProperty().get()).signum() >= 0) {\n                actionComboBox.getSelectionModel().select(2);  // transferIn\n            } else {\n                actionComboBox.getSelectionModel().select(3);  // transferOut\n            }\n        }\n\n        actionComboBox.getSelectionModel().getSelectedItem().getController().modifyTransaction(transaction);\n    }\n\n\n    private SlipControllerContainer buildCashTransferTab(final String name, final SlipType slipType) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"InvestmentTransactionPane.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final SlipController slipController = fxmlLoader.getController();\n\n            slipController.setSlipType(slipType);\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildAdjustShareTab(final String name, final TransactionType transactionType) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"AdjustSharesSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final AdjustSharesSlipController slipController = fxmlLoader.getController();\n\n            slipController.setTransactionType(transactionType);\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildBuyShareTab(final String name) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"BuyShareSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final BuyShareSlipController slipController = fxmlLoader.getController();\n\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildSellShareTab(final String name) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"SellShareSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final SellShareSlipController slipController = fxmlLoader.getController();\n\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildReinvestDividendTab(final String name) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"ReinvestDividendSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final ReinvestDividendSlipController slipController = fxmlLoader.getController();\n\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildDividendTab(final String name) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"DividendSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final DividendSlipController slipController = fxmlLoader.getController();\n\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildSplitMergeTab(final String name, final TransactionType transactionType) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"SplitMergeSharesSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final SplitMergeSharesSlipController slipController = fxmlLoader.getController();\n\n            slipController.setTransactionType(transactionType);\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    private SlipControllerContainer buildReturnOfCapitalTab(final String name) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"ReturnOfCapitalSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final ReturnOfCapitalSlipController slipController = fxmlLoader.getController();\n\n            slipController.accountProperty().bind(accountProperty());\n\n            return new SlipControllerContainer(name, slipController, pane);\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/InvestmentTransactionDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.function.Consumer;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.layout.StackPane;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * A Dialog for creating and editing new investment transactions.\n *\n * @author Craig Cavanaugh\n */\nclass InvestmentTransactionDialog extends Stage {\n\n    @FXML\n    private ComboBox<SlipControllerContainer> actionComboBox;\n\n    @FXML\n    private StackPane transactionSlips;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private Button enterButton;\n\n    @FXML\n    private Button cancelButton;\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    private final ObjectProperty<Account> accountProperty = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Consumer<Transaction>> transactionConsumer = new SimpleObjectProperty<>();\n\n    private InvestmentSlipManager investmentSlipManager;\n\n    private InvestmentTransactionDialog() {\n        FXMLUtils.loadFXML(this, \"InvestmentTransactionDialog.fxml\", ResourceUtils.getBundle());\n\n        setTitle(ResourceUtils.getString(\"Title.NewTrans\"));\n    }\n\n    private ObjectProperty<Account> accountProperty() {\n        return accountProperty;\n    }\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        enterButton.setOnAction(value -> handleEnterAction());\n        cancelButton.setOnAction(value -> handleCancelAction());\n\n        investmentSlipManager = new InvestmentSlipManager(transactionSlips, actionComboBox);\n        investmentSlipManager.accountProperty().bind(accountProperty());\n\n        actionComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                enterButton.disableProperty().bind(newValue.getController().validFormProperty().not());\n            }\n        });\n    }\n\n    private void setTransactionConsumer(final Consumer<Transaction> consumer) {\n        transactionConsumer.set(consumer);\n    }\n\n    @FXML\n    private void handleEnterAction() {\n        final Slip controller = actionComboBox.getSelectionModel().getSelectedItem().getController();\n\n        transactionConsumer.get().accept(controller.buildTransaction());\n        transactionSlips.getScene().getWindow().hide();\n    }\n\n    @FXML\n    private void handleCancelAction() {\n        transactionSlips.getScene().getWindow().hide();\n    }\n\n    private void setTransaction(final Transaction transaction) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        if (engine.isStored(transaction)) {\n            setTitle(ResourceUtils.getString(\"Title.ModifyTransaction\"));\n        }\n\n        investmentSlipManager.modifyTransaction(transaction);\n    }\n\n    public static void show(final Account account, final Transaction transaction,\n                            final Consumer<Transaction> consumer) {\n\n        final InvestmentTransactionDialog invTransDialog = new InvestmentTransactionDialog();\n\n        invTransDialog.accountProperty().set(account);\n        invTransDialog.setTransactionConsumer(consumer);\n\n        invTransDialog.setTransaction(transaction);\n\n        JavaFXUtils.runLater(() -> {\n            invTransDialog.show();\n\n            // Size and lock the height of the dialog after it has been shown\n            JavaFXUtils.runLater(() -> {\n                invTransDialog.sizeToScene();\n\n                invTransDialog.setMinHeight(invTransDialog.getHeight());\n                invTransDialog.setMaxHeight(invTransDialog.getHeight());\n            });\n\n            JavaFXUtils.runLater(() -> StageUtils.addBoundsListener(invTransDialog, InvestmentTransactionDialog.class,\n                    MainView.getPrimaryStage()));\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/InvestmentTransactionQuantityTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\n\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.text.NumericFormats;\n\n/**\n * {@code TableCell} for rendering investment transaction quantities.\n *\n * @author Craig Cavanaugh\n */\nclass InvestmentTransactionQuantityTableCell extends AbstractTransactionTableCell {\n\n    @Override\n    protected void updateItem(final BigDecimal amount, final boolean empty) {\n        super.updateItem(amount, empty);  // required\n\n        if (!empty && amount != null && getTableRow() != null) {\n\n            final Transaction transaction = getTableRow().getItem();\n\n            if (transaction instanceof InvestmentTransaction) {\n\n                final NumberFormat format\n                        = NumericFormats.getShortCommodityFormat(((InvestmentTransaction) transaction).getSecurityNode());\n\n                applyFormat(amount, format);\n            } else {\n                setText(null);\n            }\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/LiabilityRegisterPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.Objects;\n\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ButtonBar;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AmortizeObject;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\n\npublic class LiabilityRegisterPaneController extends BankRegisterPaneController {\n\n    // TODO:  CSS lookup\n    private final DoubleProperty titledPanePadding = new SimpleDoubleProperty(37);\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        buttonBar.minWidthProperty().bind(titledPane.widthProperty().subtract(titledPanePadding));\n    }\n\n    @FXML\n    private void handleAmortizeAction() {\n        final Stage dialog = new Stage(StageStyle.DECORATED);\n        dialog.initModality(Modality.WINDOW_MODAL);\n        dialog.initOwner(MainView.getPrimaryStage());\n        dialog.setTitle(ResourceUtils.getString(\"Title.AmortizationSetup\"));\n\n        final AmortizeSetupDialogController controller = FXMLUtils.loadFXML(o -> dialog.setScene(new Scene((Parent) o)),\n                \"AmortizeSetupDialog.fxml\", ResourceUtils.getBundle());\n\n        if (accountProperty().get().getAmortizeObject() != null) {\n            controller.setAmortizeObject(accountProperty().get().getAmortizeObject());\n        }\n\n        ThemeManager.applyStyleSheets(dialog.getScene());\n\n        dialog.setResizable(false);\n\n        StageUtils.addBoundsListener(dialog, AmortizeSetupDialogController.class, MainView.getPrimaryStage());\n\n        dialog.showAndWait();\n\n        if (controller.getResult()) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n            Objects.requireNonNull(engine);\n\n            if (!engine.setAmortizeObject(accountProperty().get(), controller.getAmortizeObject())) {\n                StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.AmortizationSave\"));\n            }\n        }\n    }\n\n    @FXML\n    private void handleNewPaymentAction() {\n        AmortizeObject amortizeObject = accountProperty().get().getAmortizeObject();\n\n        if (amortizeObject != null) {\n\n            final Account account = accountProperty().get();\n\n            final Stage dialog = new Stage(StageStyle.DECORATED);\n            dialog.initModality(Modality.WINDOW_MODAL);\n            dialog.initOwner(MainView.getPrimaryStage());\n            dialog.setTitle(ResourceUtils.getString(\"Title.NewTrans\"));\n\n            final DateTransNumberDialogController controller = FXMLUtils.loadFXML(o -> dialog.setScene(new Scene((Parent) o)),\n                    \"DateTransNumberDialog.fxml\", ResourceUtils.getBundle());\n\n            controller.setAccount(amortizeObject.getBankAccount());\n\n            ThemeManager.applyStyleSheets(dialog.getScene());\n\n            dialog.setResizable(false);\n            StageUtils.addBoundsListener(dialog, DateTransNumberDialogController.class, MainView.getPrimaryStage());\n\n            dialog.showAndWait();\n\n            if (!controller.getResult()) {\n                return;\n            }\n\n            final Transaction tran = amortizeObject.generateTransaction(account, controller.getDate(),\n                    controller.getNumber());\n\n            if (tran != null) {// display the transaction in the register\n                TransactionDialog.showAndWait(amortizeObject.getBankAccount(), tran, transaction -> {\n                    final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n                    Objects.requireNonNull(engine);\n\n                    engine.addTransaction(transaction);\n                });\n\n            } else {\n                StaticUIMethods.displayWarning(resources.getString(\"Message.Warn.ConfigAmortization\"));\n            }\n        } else { // could not generate the transaction\n            StaticUIMethods.displayWarning(resources.getString(\"Message.Warn.ConfigAmortization\"));\n        }\n\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/LockedBasicRegisterPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.IOException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\n\n/**\n * Basic Locked Register pane controller.\n *\n * @author Craig Cavanaugh\n */\npublic class LockedBasicRegisterPaneController extends RegisterPaneController {\n\n    @FXML\n    @Override\n    void initialize() {\n        super.initialize();\n\n        // Load the register table\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"BasicRegisterTable.fxml\"), resources);\n            registerTablePane.getChildren().add(fxmlLoader.load());\n            registerTableController.set(fxmlLoader.getController());\n        } catch (final IOException e) {\n            Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/LockedInvestmentRegisterPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport jgnash.util.LogUtil;\n\nimport java.io.IOException;\n\n/**\n * Locked Investment Register pane controller.\n *\n * @author Craig Cavanaugh\n */\npublic class LockedInvestmentRegisterPaneController extends RegisterPaneController {\n\n    @FXML\n    @Override\n    void initialize() {\n        super.initialize();\n\n        // Load the register table\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"InvestmentRegisterTable.fxml\"), resources);\n            registerTablePane.getChildren().add(fxmlLoader.load());\n            registerTableController.set(fxmlLoader.getController());\n        } catch (final IOException e) {\n            LogUtil.logSevere(LockedInvestmentRegisterPaneController.class, e);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/RegisterActions.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.File;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.scene.control.ButtonType;\nimport javafx.stage.FileChooser;\n\nimport jgnash.convert.exportantur.csv.CsvExport;\nimport jgnash.convert.exportantur.ofx.OfxExport;\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.report.poi.Workbook;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.report.AccountRegisterReport;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.uifx.views.register.reconcile.ReconcileSettingsDialogController;\nimport jgnash.util.FileUtils;\n\n/**\n * Register actions utility class.\n *\n * @author Craig Cavanaugh\n */\npublic final class RegisterActions {\n\n    private static final String EXPORT_DIR = \"exportDir\";\n\n    private static final String OFX = \"ofx\";\n\n    private static final String XLS = \"xls\";\n\n    private RegisterActions() {\n        // Utility class\n    }\n\n    static void reconcileTransactionAction(final Account account, final Transaction transaction,\n                                           final ReconciledState reconciled) {\n        if (transaction != null) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            if (engine != null) {\n                engine.setTransactionReconciled(transaction, account, reconciled);\n            }\n        }\n    }\n\n    static void deleteTransactionAction(final Transaction... transactions) {\n        if (Options.confirmOnTransactionDeleteProperty().get()) {\n            if (confirmTransactionRemoval(transactions.length).getButtonData().isCancelButton()) {\n                return;\n            }\n        }\n\n        // Move to a thread so the UI does not block\n        Thread thread = new Thread(() -> {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            if (engine != null) {\n                for (final Transaction transaction : transactions) {\n                    if (engine.removeTransaction(transaction)) {\n                        if (transaction.getAttachment() != null) {\n                            if (confirmAttachmentDeletion().getButtonData().isCancelButton()) {\n                                if (!engine.removeAttachment(transaction.getAttachment())) {\n                                    StaticUIMethods.displayError(ResourceUtils.getString(\"Message.Error.DeleteAttachment\",\n                                            transaction.getAttachment()));\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        });\n\n        thread.start();\n    }\n\n    static void duplicateTransaction(final Account account, final List<Transaction> transactions) {\n        final ResourceBundle resourceBundle = ResourceUtils.getBundle();\n\n        final String eftNumber = resourceBundle.getString(\"Item.EFT\");\n\n        for (final Transaction transaction: transactions ) {\n\n            try {\n                final Transaction clone = (Transaction) transaction.clone();\n                clone.setDate(LocalDate.now());\n\n                if (!clone.getNumber().isEmpty() && !clone.getNumber().equals(eftNumber)) {\n                    final String nextTransactionNumber = account.getNextTransactionNumber();    // may return an empty string\n                    if (!nextTransactionNumber.isEmpty()) {\n                        clone.setNumber(nextTransactionNumber);\n                    }\n                }\n\n                if (transaction instanceof InvestmentTransaction) {\n                    InvestmentTransactionDialog.show(account, clone, RegisterActions::addTransaction);\n                } else {\n                    TransactionDialog.showAndWait(account, clone, RegisterActions::addTransaction);\n                }\n            } catch (final CloneNotSupportedException e) {\n                Logger.getLogger(RegisterActions.class.getName()).log(Level.SEVERE, e.getMessage(), e);\n            }\n        }\n    }\n\n    private static void addTransaction(final Transaction transaction) {\n        if (transaction != null) {\n            final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            Objects.requireNonNull(engine);\n\n            engine.addTransaction(transaction);\n        }\n    }\n\n    public static void reconcileAccountAction(final Account account) {\n        final FXMLUtils.Pair<ReconcileSettingsDialogController> pair =\n                FXMLUtils.load(ReconcileSettingsDialogController.class.getResource(\"ReconcileSettingsDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.ReconcileSettings\"));\n\n        pair.getController().accountProperty().set(account);\n\n        pair.getStage().setResizable(false);\n        pair.getStage().showAndWait();\n    }\n\n    static void exportTransactions(final Account account, final LocalDate startDate, final LocalDate endDate) {\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final Preferences pref = Preferences.userNodeForPackage(RegisterActions.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(EXPORT_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.CsvFiles\") + \" (*.csv)\", \"*.csv\"),\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.OfxFiles\") + \" (*.ofx)\", \"*.ofx\"),\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.SpreadsheetFiles\") + \" (*.xls)\",\n                        \"*.xls\"),\n                new FileChooser.ExtensionFilter(resources.getString(\"Label.SpreadsheetFiles\") + \" (*.xlsx)\",\n                         \"*.xlsx\")\n        );\n\n        final File file = fileChooser.showSaveDialog(MainView.getPrimaryStage());\n        final File exportFile;\n\n        if (file != null) {\n            if (!FileUtils.fileHasExtension(file.getName())) {  // fix up the file name if the user did not specify it\n                final String fileExtension = fileChooser.getSelectedExtensionFilter().getExtensions().get(0).substring(1);\n                exportFile = new  File(FileUtils.stripFileExtension(file.getAbsolutePath()) + fileExtension);\n            } else {\n                exportFile = file;\n            }\n\n            pref.put(EXPORT_DIR, exportFile.getParentFile().getAbsolutePath());\n\n            final Task<Void> exportTask = new Task<>() {\n                @Override\n                protected Void call() {\n                    updateMessage(resources.getString(\"Message.PleaseWait\"));\n                    updateProgress(-1, Long.MAX_VALUE);\n\n                    if (OFX.equals(FileUtils.getFileExtension(exportFile.getName()))) {\n                        final OfxExport export = new OfxExport(account, startDate, endDate, exportFile);\n                        export.exportAccount();\n                    } else if (FileUtils.getFileExtension(exportFile.getName()).contains(XLS)) {\n                        final AbstractReportTableModel reportTableModel = AccountRegisterReport.createReportModel(account,\n                                startDate, endDate, false, \"\", \"\", true);\n\n                        Workbook.export(reportTableModel, exportFile);\n                    } else {\n                        CsvExport.exportAccount(account, startDate, endDate, exportFile.toPath());\n                    }\n                    return null;\n                }\n            };\n\n            new Thread(exportTask).start();\n\n            StaticUIMethods.displayTaskProgress(exportTask);\n        }\n    }\n\n    private static ButtonType confirmTransactionRemoval(final int count) {\n        final ResourceBundle rb = ResourceUtils.getBundle();\n\n        return StaticUIMethods.showConfirmationDialog(rb.getString(\"Title.Confirm\"),\n                count == 1 ? rb.getString(\"Message.ConfirmTransDelete\")\n                        : rb.getString(\"Message.ConfirmMultipleTransDelete\"));\n    }\n\n    private static ButtonType confirmAttachmentDeletion() {\n        final ResourceBundle rb = ResourceUtils.getBundle();\n\n        return StaticUIMethods.showConfirmationDialog(rb.getString(\"Title.DeleteAttachment\"),\n                rb.getString(\"Question.DeleteAttachment\"));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/RegisterFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.ResourceBundle;\n\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.uifx.Options;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Factory class for constructing register tables and controlling global options for registers.\n *\n * @author Craig Cavanaugh\n */\npublic class RegisterFactory {\n    private static final ResourceBundle rb = ResourceUtils.getBundle();\n\n    private static final String COLUMN_DATE = \"Column.Date\";\n    private static final String COLUMN_NUM = \"Column.Num\";\n    private static final String COLUMN_PAYEE = \"Column.Payee\";\n    private static final String COLUMN_MEMO = \"Column.Memo\";\n    private static final String COLUMN_ACCOUNT = \"Column.Account\";\n    private static final String COLUMN_CLR = \"Column.Clr\";\n    private static final String COLUMN_DEPOSIT = \"Column.Deposit\";\n    private static final String COLUMN_WITHDRAWAL = \"Column.Withdrawal\";\n    private static final String COLUMN_BALANCE = \"Column.Balance\";\n    private static final String COLUMN_INCREASE = \"Column.Increase\";\n    private static final String COLUMN_DECREASE = \"Column.Decrease\";\n    private static final String COLUMN_CHARGE = \"Column.Charge\";\n    private static final String COLUMN_DEBIT = \"Column.Debit\";\n    private static final String COLUMN_CREDIT = \"Column.Credit\";\n    private static final String COLUMN_REBATE = \"Column.Rebate\";\n    private static final String COLUMN_EXPENSE = \"Column.Expense\";\n    private static final String COLUMN_INCOME = \"Column.Income\";\n    private static final String COLUMN_RECEIVE = \"Column.Receive\";\n    private static final String COLUMN_SPEND = \"Column.Spend\";\n    private static final String COLUMN_PAYMENT = \"Column.Payment\";\n    private static final String COLUMN_ACTION = \"Column.Action\";\n    private static final String COLUMN_INVESTMENT = \"Column.Investment\";\n    private static final String COLUMN_PRICE = \"Column.Price\";\n    private static final String COLUMN_TIMESTAMP = \"Column.Timestamp\";\n    private static final String COLUMN_TOTAL = \"Column.Total\";\n    private static final String COLUMN_QUANTITY = \"Column.Quantity\";\n    private static final String COLUMN_GAIN = \"Column.Gain\";\n    private static final String COLUMN_LOSS = \"Column.Loss\";\n\n    private static final String[] BANK_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP),\n            rb.getString(COLUMN_NUM), rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO),\n            rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR), rb.getString(COLUMN_DEPOSIT),\n            rb.getString(COLUMN_WITHDRAWAL), rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] GENERIC_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_INCREASE), rb.getString(COLUMN_DECREASE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] CASH_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_RECEIVE), rb.getString(COLUMN_SPEND),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] EXPENSE_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_EXPENSE), rb.getString(COLUMN_REBATE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] INCOME_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_CHARGE), rb.getString(COLUMN_INCOME),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] CREDIT_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_PAYMENT), rb.getString(COLUMN_CHARGE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] EQUITY_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_DECREASE), rb.getString(COLUMN_INCREASE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] LIABILITY_NAMES = EQUITY_NAMES;\n\n    private static final String[] ACCOUNTING_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_NUM),\n            rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_ACCOUNT),\n            rb.getString(COLUMN_CLR), rb.getString(COLUMN_DEBIT), rb.getString(COLUMN_CREDIT),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] INVESTMENT_NAMES = { rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP), rb.getString(COLUMN_ACTION),\n            rb.getString(COLUMN_INVESTMENT), rb.getString(COLUMN_MEMO), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_QUANTITY), rb.getString(COLUMN_PRICE), rb.getString(COLUMN_TOTAL) };\n\n    private static final String[] SPLIT_ACCOUNTING_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_DEBIT), rb.getString(COLUMN_CREDIT),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_CREDIT_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_PAYMENT), rb.getString(COLUMN_CHARGE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_EXPENSE_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_EXPENSE), rb.getString(COLUMN_REBATE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_INCOME_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_CHARGE), rb.getString(COLUMN_INCOME),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_CASH_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_RECEIVE), rb.getString(COLUMN_SPEND),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_EQUITY_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_DECREASE), rb.getString(COLUMN_INCREASE),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_LIABILITY_NAMES = SPLIT_EQUITY_NAMES;\n\n    private static final String[] SPLIT_BANK_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_DEPOSIT), rb.getString(COLUMN_WITHDRAWAL),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_GAIN_LOSS_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_GAIN), rb.getString(COLUMN_LOSS),\n            rb.getString(COLUMN_BALANCE) };\n\n    private static final String[] SPLIT_GENERIC_NAMES = { rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR),\n            rb.getString(COLUMN_MEMO), rb.getString(COLUMN_DEPOSIT), rb.getString(COLUMN_WITHDRAWAL),\n            rb.getString(COLUMN_BALANCE) };\n\n    private RegisterFactory() {\n        // Utility class\n    }\n\n    static String[] getGainLossSplitColumnName() {\n        String[] names; // reference to the correct column names\n\n        if (Options.useAccountingTermsProperty().get()) {\n            names = SPLIT_ACCOUNTING_NAMES;\n        } else {\n            names = SPLIT_GAIN_LOSS_NAMES;\n        }\n\n        return names;\n    }\n\n    static String[] getSplitColumnNames(@NotNull final AccountType accountType) {\n        String[] names; // reference to the correct column names\n\n        if (Options.useAccountingTermsProperty().get()) {\n            names = SPLIT_ACCOUNTING_NAMES;\n        } else {\n            if (accountType == AccountType.CREDIT) {\n                names = SPLIT_CREDIT_NAMES;\n            } else if (accountType == AccountType.EXPENSE) {\n                names = SPLIT_EXPENSE_NAMES;\n            } else if (accountType == AccountType.INCOME) {\n                names = SPLIT_INCOME_NAMES;\n            } else if (accountType == AccountType.CASH) {\n                names = SPLIT_CASH_NAMES;\n            } else if (accountType == AccountType.EQUITY) {\n                names = SPLIT_EQUITY_NAMES;\n            } else if (accountType == AccountType.LIABILITY) {\n                names = SPLIT_LIABILITY_NAMES;\n            } else if (accountType.getAccountGroup() == AccountGroup.ASSET) {\n                names = SPLIT_BANK_NAMES;\n            } else {    // Investment accounts\n                names = SPLIT_GENERIC_NAMES;\n            }\n        }\n\n        return names;\n    }\n\n    public static String[] getColumnNames(@NotNull final AccountType accountType) {\n        String[] names; // reference to the correct column names\n\n        if (Options.useAccountingTermsProperty().get()) {\n            names = ACCOUNTING_NAMES;\n        } else {\n            if (accountType == AccountType.CREDIT) {\n                names = CREDIT_NAMES;\n            } else if (accountType == AccountType.EXPENSE) {\n                names = EXPENSE_NAMES;\n            } else if (accountType == AccountType.INCOME) {\n                names = INCOME_NAMES;\n            } else if (accountType == AccountType.CASH) {\n                names = CASH_NAMES;\n            } else if (accountType == AccountType.EQUITY) {\n                names = EQUITY_NAMES;\n            } else if (accountType == AccountType.LIABILITY) {\n                names = LIABILITY_NAMES;\n            } else if (accountType.getAccountGroup() == AccountGroup.ASSET) {\n                names = BANK_NAMES;\n            } else if (accountType.getAccountGroup() == AccountGroup.INVEST) {\n                names = INVESTMENT_NAMES;\n            } else {\n                names = GENERIC_NAMES;\n            }\n        }\n\n        return names;\n    }\n\n    /**\n     * Generates tab names for transaction forms.\n     *\n     * @param accountType {@code AccountType} to generate tab names for\n     * @return tab names with increase name at 0 and decrease name at 1\n     */\n    public static String[] getCreditDebitTabNames(final AccountType accountType) {\n\n        if (Options.useAccountingTermsProperty().get()) {\n            if (accountType.getAccountGroup() == AccountGroup.INCOME\n                    || accountType.getAccountGroup() == AccountGroup.EXPENSE\n                    || accountType.getAccountGroup() == AccountGroup.ASSET\n                    || accountType.getAccountGroup() == AccountGroup.INVEST\n                    || accountType.getAccountGroup() == AccountGroup.LIABILITY) {\n                return new String[] { rb.getString(COLUMN_DEBIT), rb.getString(COLUMN_CREDIT) };\n            }\n            return new String[] { rb.getString(COLUMN_CREDIT), rb.getString(COLUMN_DEBIT) };\n        }\n        if (accountType == AccountType.CREDIT) {\n            return new String[] { rb.getString(COLUMN_PAYMENT), rb.getString(COLUMN_CHARGE) };\n        } else if (accountType == AccountType.EXPENSE) {\n            return new String[] { rb.getString(COLUMN_EXPENSE), rb.getString(COLUMN_REBATE) };\n        } else if (accountType == AccountType.INCOME) {\n            return new String[] { rb.getString(COLUMN_CHARGE), rb.getString(COLUMN_INCOME) };\n        } else if (accountType == AccountType.CASH) {\n            return new String[] { rb.getString(COLUMN_RECEIVE), rb.getString(COLUMN_SPEND) };\n        } else if (accountType == AccountType.LIABILITY) {\n            return new String[] { rb.getString(COLUMN_DECREASE), rb.getString(COLUMN_INCREASE) };\n        } else if (accountType == AccountType.EQUITY) {\n            return new String[] { rb.getString(COLUMN_DECREASE), rb.getString(COLUMN_INCREASE) };\n        } else if (accountType.getAccountGroup() == AccountGroup.ASSET) {\n            return new String[] { rb.getString(COLUMN_DEPOSIT), rb.getString(COLUMN_WITHDRAWAL) };\n        } else {\n            return new String[] { rb.getString(COLUMN_INCREASE), rb.getString(COLUMN_DECREASE) };\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/RegisterPaneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TitledPane;\nimport javafx.scene.layout.StackPane;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\n\n/**\n * Register pane controller.\n *\n * @author Craig Cavanaugh\n */\nabstract class RegisterPaneController {\n\n    @FXML\n    protected Button newButton;\n\n    @FXML\n    protected Button duplicateButton;\n\n    @FXML\n    protected Button deleteButton;\n\n    /**\n     * The register table and labels should be loaded into this pane.\n     */\n    @FXML\n    protected StackPane registerTablePane;\n\n    @FXML\n    protected ResourceBundle resources;\n\n    @FXML\n    protected TitledPane titledPane;\n\n    /**\n     * Active account for the pane.\n     */\n    private final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    /**\n     * This will be bound to the register table selection.\n     */\n    final ObjectProperty<Transaction> selectedTransaction = new SimpleObjectProperty<>();\n\n    final ObjectProperty<RegisterTableController> registerTableController = new SimpleObjectProperty<>();\n\n    ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    @FXML\n    void initialize() {\n\n        /*\n         * The Buttons may be null depending on the form tha was loaded with this controller\n         */\n\n        // Buttons should not be enabled if a transaction is not selected\n        if (deleteButton != null) {\n            deleteButton.disableProperty().bind(selectedTransaction.isNull());\n        }\n\n        if (duplicateButton != null) {\n            duplicateButton.disableProperty().bind(selectedTransaction.isNull());\n        }\n\n        // Clear the table selection\n        if (newButton != null) {\n            newButton.setOnAction(event -> registerTableController.get().clearTableSelection());\n\n            // disable if the titledPane is collapsed\n            newButton.disableProperty().bind(titledPane.expandedProperty().not());\n        }\n\n        // When changed, bind the selected transaction and account properties\n        registerTableController.addListener((observable, oldValue, newValue) -> {\n\n            // Bind transaction selection to the register table controller\n            selectedTransaction.bind(newValue.selectedTransactionProperty());\n\n            // Bind the register pane to this account property\n            newValue.accountProperty().bind(accountProperty());\n        });\n\n        // When changed, call for transaction modification if not null\n        selectedTransaction.addListener((observable, oldValue, newValue) -> {\n\n            /* Push to the end of the application thread to allow other UI controls to update before\n            * updating many transaction form controls */\n            JavaFXUtils.runLater(() -> {\n                if (newValue != null) {\n                    modifyTransaction(newValue);\n                } else {\n                    clearForm(); // selection was forcibly cleared, better clear the form\n                }\n            });\n        });\n\n        if (titledPane != null) {   // make sure we don't have a locked instance\n            titledPane.animatedProperty().bind(Options.animationsEnabledProperty());\n        }\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        registerTableController.get().deleteTransactions();\n    }\n\n    /**\n     * Default empty implementation to modify a transaction when selected.\n     *\n     * @param transaction {@code Transaction} to be modified\n     */\n    void modifyTransaction(@NotNull final Transaction transaction) {\n\n    }\n\n    void selectTransaction(@NotNull final Transaction transaction) {\n        registerTableController.get().selectTransaction(transaction);\n    }\n\n    /**\n     * Default empty implementation.\n     */\n    void clearForm() {\n\n    }\n\n    @FXML\n    void handleJumpAction() {\n        registerTableController.get().handleJumpAction();\n    }\n\n    @FXML\n    private void handleDuplicateAction() {\n        clearForm();\n\n        RegisterActions.duplicateTransaction(account.get(), registerTableController.get().getSelectedTransactions());\n\n        // Request focus as it may have been lost\n        MainView.requestFocus();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/RegisterStage.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.beans.property.ListProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleListProperty;\nimport javafx.collections.FXCollections;\nimport javafx.scene.Parent;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.stage.Screen;\nimport javafx.stage.Stage;\nimport javafx.stage.StageStyle;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.Transaction;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.Version;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.util.NotNull;\n\n/**\n * A Stage that displays a single account register. Size and position is preserved.\n *\n * @author Craig Cavanaugh\n */\npublic class RegisterStage extends Stage {\n\n    /**\n     * Static list of register stages.\n     */\n    final private static ListProperty<RegisterStage> registerStageList\n            = new SimpleListProperty<>(FXCollections.observableArrayList());\n\n    private static final double SCALE_FACTOR = 0.7;\n\n    final private ReadOnlyObjectWrapper<Account> account = new ReadOnlyObjectWrapper<>();\n\n    private final RegisterPaneController controller;\n\n    private RegisterStage(@NotNull final Account account) {\n        super(StageStyle.DECORATED);\n\n        this.account.set(account);\n\n        final String formResource;\n\n        if (account.isLocked()) {\n            if (account.memberOf(AccountGroup.INVEST)) {\n                formResource = \"LockedInvestmentRegisterPane.fxml\";\n            } else {\n                formResource = \"LockedBasicRegisterPane.fxml\";\n            }\n        } else {\n            if (account.memberOf(AccountGroup.INVEST)) {\n                formResource = \"InvestmentRegisterPane.fxml\";\n            } else {\n                formResource = \"BasicRegisterPane.fxml\";\n            }\n        }\n\n        controller = FXMLUtils.loadFXML(scene -> setScene(new Scene((Parent) scene)), formResource,\n                ResourceUtils.getBundle());\n\n        ThemeManager.applyStyleSheets(getScene());\n\n        getIcons().add(StaticUIMethods.getApplicationIcon());\n\n        // handle CTRL-F4\n        getScene().setOnKeyPressed(event -> {\n            if (event.isControlDown() && event.getCode() == KeyCode.F4) {\n                close();\n            }\n        });\n\n        double minWidth = Double.MAX_VALUE;\n        double minHeight = Double.MAX_VALUE;\n\n        for (final Screen screen : Screen.getScreens()) {\n            minWidth = Math.min(minWidth, screen.getVisualBounds().getWidth());\n            minHeight = Math.min(minHeight, screen.getVisualBounds().getHeight());\n        }\n\n        setWidth(minWidth * SCALE_FACTOR);\n        setHeight(minHeight * SCALE_FACTOR);\n\n        // Push the account to the controller at the end of the application thread\n        // Must use JavaFXUtils instead of Platform to prevent a race condition\n        JavaFXUtils.runLater(() -> controller.accountProperty().set(account));\n\n        updateTitle(account);\n\n        StageUtils.addBoundsListener(this, account.getUuid().toString(), null);\n\n        registerStageList.get().add(this);\n\n        setOnHidden(event -> registerStageList.get().remove(RegisterStage.this));\n    }\n\n    public static RegisterStage getRegisterStage(@NotNull final Account account) {\n\n        // look for an existing stage first\n        for (final RegisterStage registerStage : registerStageList) {\n            if (registerStage.account.get().equals(account)) {\n                registerStage.requestFocus();\n                return registerStage;\n            }\n        }\n\n        return new RegisterStage(account);\n    }\n\n    public void show(final Transaction transaction) {\n        show();\n        JavaFXUtils.runLater(() -> controller.selectTransaction(transaction));\n    }\n\n    public ReadOnlyObjectProperty<Account> accountProperty() {\n        return account.getReadOnlyProperty();\n    }\n\n    public static ListProperty<RegisterStage> registerStageList() {\n        return registerStageList;\n    }\n\n    private void updateTitle(final Account account) {\n        setTitle(Version.getAppName() + \" - \" + account.getPathName());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/RegisterTableController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Predicate;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.IntegerProperty;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectProperty;\nimport javafx.beans.property.ReadOnlyObjectWrapper;\nimport javafx.beans.property.SimpleIntegerProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.collections.ObservableList;\nimport javafx.collections.SetChangeListener;\nimport javafx.collections.WeakListChangeListener;\nimport javafx.collections.WeakSetChangeListener;\nimport javafx.collections.transformation.FilteredList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.ContextMenu;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.control.SeparatorMenuItem;\nimport javafx.scene.control.TableRow;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.TextField;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.input.Clipboard;\nimport javafx.scene.input.ClipboardContent;\nimport javafx.util.Callback;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Tag;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.TableViewEx;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.TableViewManager;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.uifx.views.AccountBalanceDisplayMode;\nimport jgnash.uifx.views.recurring.RecurringEntryDialog;\nimport jgnash.util.function.MemoPredicate;\nimport jgnash.util.function.PayeePredicate;\nimport jgnash.util.function.ReconciledPredicate;\nimport jgnash.util.function.TagPredicate;\nimport jgnash.util.function.TransactionAgePredicate;\n\n/**\n * Abstract Register Table with stats controller.\n *\n * @author Craig Cavanaugh\n */\nabstract class RegisterTableController {\n\n    private static final String PREF_NODE_USER_ROOT = \"/jgnash/uifx/views/register\";\n\n    /**\n     * Active account for the pane.\n     */\n    final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private final ReadOnlyObjectWrapper<Transaction> selectedTransaction = new ReadOnlyObjectWrapper<>();\n\n    /**\n     * This is the master list of transactions.\n     */\n    private final ObservableList<Transaction> observableTransactions = FXCollections.observableArrayList();\n\n    /**\n     * Filters may be applied to this list.\n     */\n    private final FilteredList<Transaction> filteredTransactionList = new FilteredList<>(observableTransactions,\n            transaction -> true);\n\n    /**\n     * Sorted list of transactions.\n     */\n    final SortedList<Transaction> sortedList = new SortedList<>(filteredTransactionList);\n\n    private final MessageBusHandler messageBusHandler = new MessageBusHandler();\n\n    private final AccountPropertyWrapper accountPropertyWrapper = new AccountPropertyWrapper();\n\n    // Used for selection summary tooltip\n    private final IntegerProperty selectionSize = new SimpleIntegerProperty(0);\n\n    // Used for selection summary tooltip\n    private final Tooltip selectionSummaryTooltip = new Tooltip();\n\n    /**\n     * Listens for changes to the font scale\n     */\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Number> fontScaleListener;\n\n    @FXML\n    protected TableViewEx<Transaction> tableView;\n\n    @FXML\n    protected Label balanceLabel;\n\n    @FXML\n    protected Label accountNameLabel;\n\n    @FXML\n    protected ResourceBundle resources;\n\n    @FXML\n    protected ComboBox<ReconciledStateEnum> reconciledStateFilterComboBox;\n\n    @FXML\n    protected ComboBox<AgeEnum> transactionAgeFilterComboBox;\n\n    @FXML\n    protected TextField memoFilterTextField;\n\n    @FXML\n    protected TextField payeeFilterTextField;\n\n    @FXML\n    private TransactionTagPane tagPane;\n\n    TableViewManager<Transaction> tableViewManager;\n\n    // Used for formatting of the selection summary tooltip\n    private NumberFormat numberFormat = NumberFormat.getNumberInstance();\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Object> filterChangeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Number> selectionSizeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private SetChangeListener<Tag> tagSetChangeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ListChangeListener<Transaction> selectedItemsListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<AccountBalanceDisplayMode> accountBalanceDisplayModeChangeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<String> formatListener;\n\n    @FXML\n    void initialize() {\n\n        // table view displays the sorted list of data. The comparator property must be\n        // bound\n        tableView.setItems(sortedList);\n        sortedList.comparatorProperty().bind(tableView.comparatorProperty());\n\n        // register the copy to clipboard function\n        tableView.setClipBoardStringFunction(this::transactionToExcel);\n\n        // Bind the account property\n        getAccountPropertyWrapper().accountProperty().bind(account);\n\n        accountNameLabel.textProperty().bind(getAccountPropertyWrapper().accountNameProperty());\n        balanceLabel.textProperty().bind(getAccountPropertyWrapper().accountBalanceProperty());\n\n        tableView.setTableMenuButtonVisible(true);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        // hide the horizontal scrollbar and prevent ghosting\n        tableView.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS);\n\n        // Load the table on change and set the row factory if the account in not locked\n        accountProperty().addListener((observable, oldValue, newValue) -> {\n            loadAccount();\n\n            if (!newValue.isLocked()) {\n                tableView.setRowFactory(new TransactionRowFactory());\n            }\n\n            numberFormat = NumericFormats.getFullCommodityFormat(newValue.getCurrencyNode());\n        });\n\n        selectedTransaction.bind(tableView.getSelectionModel().selectedItemProperty());\n\n        // Update the selection size property when the selection list changes\n        selectedItemsListener = c -> selectionSize.set(tableView.getSelectionModel().getSelectedIndices().size());\n\n        tableView.getSelectionModel().getSelectedItems().addListener(new WeakListChangeListener<>(selectedItemsListener));\n\n        // updates a tooltip based on current selection\n        selectionSizeListener = (observable, oldValue, newValue) -> {\n            if ((Integer) newValue > 1) {\n                final List<Transaction> transactions = new ArrayList<>(\n                        tableView.getSelectionModel().getSelectedItems());\n                BigDecimal total = BigDecimal.ZERO;\n\n                for (final Transaction transaction : transactions) {\n                    if (transaction != null) {\n                        total = total.add(transaction.getAmount(account.get()));\n                    }\n                }\n                selectionSummaryTooltip.setText(numberFormat.format(AccountBalanceDisplayManager\n                                                                            .convertToSelectedBalanceMode(account.get().getAccountType(), total)));\n            } else {\n                selectionSummaryTooltip.setText(null);\n            }\n        };\n\n        selectionSize.addListener(new WeakChangeListener<>(selectionSizeListener));\n\n        accountBalanceDisplayModeChangeListener = (observable, oldValue, newValue) -> tableView.refresh();\n\n        // For the table view to refresh itself if the mode changes\n        AccountBalanceDisplayManager.accountBalanceDisplayMode()\n                .addListener(new WeakChangeListener<>(accountBalanceDisplayModeChangeListener));\n\n        reconciledStateFilterComboBox.getItems().addAll(ReconciledStateEnum.values());\n        reconciledStateFilterComboBox.setValue(ReconciledStateEnum.ALL);\n\n        transactionAgeFilterComboBox.getItems().addAll(AgeEnum.values());\n        transactionAgeFilterComboBox.setValue(AgeEnum.ALL);\n\n        filterChangeListener = (observable, oldValue, newValue) -> handleFilterChange();\n        tagSetChangeListener = change -> handleFilterChange();\n\n        reconciledStateFilterComboBox.valueProperty().addListener(new WeakChangeListener<>(filterChangeListener));\n        transactionAgeFilterComboBox.valueProperty().addListener(new WeakChangeListener<>(filterChangeListener));\n\n        // Rebuild filters when regex properties change\n        Options.regexForFiltersProperty().addListener(new WeakChangeListener<>(filterChangeListener));\n\n        if (memoFilterTextField != null) { // memo filter may not have been initialized for all register types\n            memoFilterTextField.textProperty().addListener(new WeakChangeListener<>(filterChangeListener));\n        }\n\n        if (payeeFilterTextField != null) { // payee filter may not have been initialized for all register types\n            payeeFilterTextField.textProperty().addListener(new WeakChangeListener<>(filterChangeListener));\n        }\n\n        tagPane.getSelectedTags().addListener(new WeakSetChangeListener<>(tagSetChangeListener));\n\n        // Repack the table if the font scale changes, must use a weak listener to prevent memory leaks\n        fontScaleListener = (observable, oldValue, newValue) -> tableViewManager.packTable();\n        ThemeManager.fontScaleProperty().addListener(new WeakChangeListener<>(fontScaleListener));\n\n        // Listen for changes to formatting preferences and force and update\n        formatListener = (observable, oldValue, newValue) -> {\n            tableView.refresh();\n            JavaFXUtils.runLater(tableViewManager::packTable);\n        };\n\n        Options.fullNumericFormatProperty().addListener(new WeakChangeListener<>(formatListener));\n        Options.shortNumericFormatProperty().addListener(new WeakChangeListener<>(formatListener));\n        Options.shortDateFormatProperty().addListener(new WeakChangeListener<>(formatListener));\n\n        // Listen for transaction events\n        MessageBus.getInstance().registerListener(messageBusHandler, MessageChannel.TRANSACTION);\n    }\n\n    private void handleFilterChange() {\n        Predicate<Transaction> predicate = new ReconciledPredicate(account.get(),\n                reconciledStateFilterComboBox.valueProperty().get().getReconciledState()).and(\n                new TransactionAgePredicate(transactionAgeFilterComboBox.valueProperty().get().getChronoUnit(),\n                        transactionAgeFilterComboBox.valueProperty().get().getAge()));\n\n        if (memoFilterTextField != null) {\n            predicate = predicate\n                                .and(new MemoPredicate(memoFilterTextField.getText(), Options.regexForFiltersProperty().get()));\n        }\n\n        if (payeeFilterTextField != null) {\n            predicate = predicate\n                                .and(new PayeePredicate(payeeFilterTextField.getText(), Options.regexForFiltersProperty().get()));\n        }\n\n        predicate = predicate.and(new TagPredicate(tagPane.getSelectedTags()));\n\n        filteredTransactionList.setPredicate(predicate);\n    }\n\n    private void loadAccount() {\n        tableViewManager = new TableViewManager<>(tableView, PREF_NODE_USER_ROOT);\n        tableViewManager.setPreferenceKeyFactory(() -> accountProperty().get().getUuid().toString());\n        tableViewManager.setColumnWeightFactory(getColumnWeightFactory());\n        tableViewManager.setDefaultColumnVisibilityFactory(getColumnVisibilityFactory());\n        tableViewManager.manualColumnPackingProperty().bind(Options.autoPackTablesProperty().not());\n\n        buildTable();\n\n        /*\n         * push to the front of the application thread to ensure table build is complete\n         * before data is loaded this prevents inconsistent and random behavior for\n         * column sizing\n         */\n        JavaFXUtils.runNow(this::loadTable);\n    }\n\n    abstract Callback<Integer, Double> getColumnWeightFactory();\n\n    abstract Callback<Integer, Boolean> getColumnVisibilityFactory();\n\n    ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    ReadOnlyObjectProperty<Transaction> selectedTransactionProperty() {\n        return selectedTransaction.getReadOnlyProperty();\n    }\n\n    void clearTableSelection() {\n        tableView.getSelectionModel().clearSelection();\n    }\n\n    AccountPropertyWrapper getAccountPropertyWrapper() {\n        return accountPropertyWrapper;\n    }\n\n    /**\n     * Scrolls the view to ensure selection visibility. If possible, the next index\n     * is shown for improved visual appearance.\n     *\n     * @param transaction transaction to show in table\n     */\n    private void scrollToTransaction(final Transaction transaction) {\n        final int index = tableView.getItems().indexOf(transaction);\n\n        if (index > 0) {\n            tableView.scrollTo(index - 1);\n        } else {\n            tableView.scrollTo(transaction);\n        }\n    }\n\n    /**\n     * Ensures the transaction is visible and selects it.\n     *\n     * @param transaction Transaction that needs to be visible in the view\n     */\n    void selectTransaction(final Transaction transaction) {\n        scrollToTransaction(transaction);\n        tableView.getSelectionModel().select(transaction);\n\n        // The table needs to be focused for the row selection to highlight\n        JavaFXUtils.runLater(tableView::requestFocus);\n    }\n\n    protected abstract void buildTable();\n\n    private void loadTable() {\n        observableTransactions.clear();\n\n        if (account.get() != null) {\n            observableTransactions.addAll(account.get().getSortedTransactionList());\n\n            JavaFXUtils.runLater(() -> { // table view many not be ready, push to end of the Platform thread\n                tableViewManager.restoreLayout(); // required for table view manager to work\n                tableView.scrollTo(observableTransactions.size()); // scroll to the end of the table\n\n                // formats have changed, force a full recalculation\n                if (Options.getLastFormatChange() >= tableViewManager.getTimeStamp()) {\n                    tableViewManager.packTable();\n                }\n            });\n        }\n    }\n\n    void manuallyPackTable() {\n        tableViewManager.packTable();\n    }\n\n    List<Transaction> getSelectedTransactions() {\n        return tableView.getSelectionModel().getSelectedItems();\n    }\n\n    void deleteTransactions() {\n        final List<Transaction> transactionList = tableView.getSelectionModel().getSelectedItems();\n\n        RegisterActions.deleteTransactionAction(transactionList.toArray(new Transaction[0]));\n    }\n\n    private void duplicateTransactions() {\n        final List<Transaction> transactionList = tableView.getSelectionModel().getSelectedItems();\n\n        RegisterActions.duplicateTransaction(account.get(), transactionList);\n    }\n\n    private void handleCreateNewReminder() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final Reminder reminder = Engine.createDefaultReminder(selectedTransaction.get(), account.get());\n\n        final Optional<Reminder> optional = RecurringEntryDialog.showAndWait(reminder);\n\n        optional.ifPresent(engine::addReminder);\n    }\n\n    void handleJumpAction() {\n        Transaction t = selectedTransaction.get();\n\n        if (t != null) {\n            if (t.getTransactionType() == TransactionType.DOUBLEENTRY) {\n                final Set<Account> set = t.getAccounts();\n                set.stream().filter(a -> !account.get().equals(a))\n                        .forEach(account -> jump(account, t));\n            } else if (t.getTransactionType() == TransactionType.SPLITENTRY) {\n                final Account common = t.getCommonAccount();\n\n                if (!account.get().equals(common)) {\n                    jump(common, t);\n                }\n            } else if (t instanceof InvestmentTransaction) {\n                final Account invest = ((InvestmentTransaction) t).getInvestmentAccount();\n\n                if (!account.get().equals(invest)) {\n                    jump(invest, t);\n                }\n            }\n        }\n    }\n\n    private void jump(final Account account, final Transaction transaction) {\n        JavaFXUtils.runAndWait(() -> {  // ensure the stage is shown before showing the transaction\n            RegisterStage registerStage = RegisterStage.getRegisterStage(account);\n            JavaFXUtils.runLater(() -> registerStage.show(transaction));\n        });\n    }\n\n    private void handleCopyToClipboard() {\n        final List<Transaction> transactionList = tableView.getSelectionModel().getSelectedItems();\n\n        if (transactionList.size() > 0) {\n            final Clipboard clipboard = Clipboard.getSystemClipboard();\n            final ClipboardContent content = new ClipboardContent();\n            final StringBuilder builder = new StringBuilder();\n\n            for (final Transaction transaction : transactionList) {\n                builder.append(transactionToExcel(transaction));\n                builder.append('\\n');\n            }\n\n            content.putString(builder.toString());\n            clipboard.setContent(content);\n        }\n    }\n\n    private String transactionToExcel(final Transaction transaction) {\n        final StringBuilder builder = new StringBuilder();\n        final DateTimeFormatter dateFormatter = DateUtils.getExcelDateFormatter();\n\n        String account;\n\n        if (transaction instanceof InvestmentTransaction) {\n            account = (((InvestmentTransaction) transaction).getInvestmentAccount().getName());\n        } else {\n            int count = transaction.size();\n            if (count > 1) {\n                account = \"[ \" + count + \" \" + resources.getString(\"Button.Splits\") + \" ]\";\n            } else {\n                Account creditAccount = transaction.getTransactionEntries().get(0).getCreditAccount();\n                if (creditAccount != accountProperty().get()) {\n                    account = creditAccount.getName();\n                } else {\n                    account = transaction.getTransactionEntries().get(0).getDebitAccount().getName();\n                }\n            }\n        }\n\n        // date, number, payee, memo, account, clr, amount, timestamp\n        builder.append(dateFormatter.format(transaction.getLocalDate()));\n        builder.append('\\t');\n        builder.append(transaction.getNumber());\n        builder.append('\\t');\n        builder.append(transaction.getPayee());\n        builder.append('\\t');\n        builder.append(transaction.getMemo());\n        builder.append('\\t');\n        builder.append(account);\n        builder.append('\\t');\n        builder.append(transaction.getReconciled(transaction.getCommonAccount()).toString());\n        builder.append('\\t');\n        builder.append(transaction.getAmount(transaction.getCommonAccount()).toPlainString());\n\n        return builder.toString();\n    }\n\n    @FXML\n    protected void handleResetFilters() {\n\n        JavaFXUtils.runLater(() -> {\n            transactionAgeFilterComboBox.setValue(AgeEnum.ALL);\n            reconciledStateFilterComboBox.setValue(ReconciledStateEnum.ALL);\n\n            if (memoFilterTextField != null) {\n                memoFilterTextField.setText(\"\");\n            }\n\n            if (payeeFilterTextField != null) {\n                payeeFilterTextField.setText(\"\");\n            }\n\n            tagPane.clearSelectedTags();\n        });\n    }\n\n    private enum ReconciledStateEnum {\n        ALL(ResourceUtils.getString(\"Button.AnyStatus\"), null),\n        CLEARED(ResourceUtils.getString(\"Button.Cleared\"), ReconciledState.CLEARED),\n        NOT_RECONCILED(ResourceUtils.getString(\"Button.NotReconciled\"), ReconciledState.NOT_RECONCILED),\n        RECONCILED(ResourceUtils.getString(\"Button.Reconciled\"), ReconciledState.RECONCILED);\n\n        private final String description;\n\n        private final ReconciledState reconciledState;\n\n        ReconciledStateEnum(final String description, final ReconciledState reconciledState) {\n            this.description = description;\n            this.reconciledState = reconciledState;\n        }\n\n        public ReconciledState getReconciledState() {\n            return reconciledState;\n        }\n\n        @Override\n        public String toString() {\n            return description;\n        }\n    }\n\n    private enum AgeEnum {\n        ALL(ResourceUtils.getString(\"Button.AllDates\"), ChronoUnit.FOREVER, 0),\n        Year(ResourceUtils.getString(\"Button.Last12Months\"), ChronoUnit.MONTHS, 12),\n        _120(ResourceUtils.getString(\"Button.Last120Days\"), ChronoUnit.DAYS, 120),\n        _90(ResourceUtils.getString(\"Button.Last90Days\"), ChronoUnit.DAYS, 90),\n        _60(ResourceUtils.getString(\"Button.Last60Days\"), ChronoUnit.DAYS, 60),\n        _30(ResourceUtils.getString(\"Button.Last30Days\"), ChronoUnit.DAYS, 30);\n\n        private final String description;\n\n        private final int age;\n\n        private final ChronoUnit chronoUnit;\n\n        AgeEnum(final String description, final ChronoUnit chronoUnit, final int age) {\n            this.description = description;\n            this.chronoUnit = chronoUnit;\n            this.age = age;\n        }\n\n        public int getAge() {\n            return age;\n        }\n\n        public ChronoUnit getChronoUnit() {\n            return chronoUnit;\n        }\n\n        @Override\n        public String toString() {\n            return description;\n        }\n    }\n\n    private class TransactionRowFactory implements Callback<TableView<Transaction>, TableRow<Transaction>> {\n\n        @Override\n        public TableRow<Transaction> call(final TableView<Transaction> param) {\n\n            final TableRow<Transaction> row = new TableRow<>();\n            final ContextMenu rowMenu = new ContextMenu();\n\n            final Menu markedAs = new Menu(resources.getString(\"Menu.MarkAs.Name\"));\n            final MenuItem markAsClearedItem = new MenuItem(resources.getString(\"Menu.Cleared.Name\"));\n            markAsClearedItem.setOnAction(event -> RegisterActions.reconcileTransactionAction(account.get(),\n                    row.getItem(), ReconciledState.CLEARED));\n\n            final MenuItem markAsReconciledItem = new MenuItem(resources.getString(\"Menu.Reconciled.Name\"));\n            markAsReconciledItem.setOnAction(event -> RegisterActions.reconcileTransactionAction(account.get(),\n                    row.getItem(), ReconciledState.RECONCILED));\n\n            final MenuItem markAsUnreconciledItem = new MenuItem(resources.getString(\"Menu.Unreconciled.Name\"));\n            markAsUnreconciledItem.setOnAction(event -> RegisterActions.reconcileTransactionAction(account.get(),\n                    row.getItem(), ReconciledState.NOT_RECONCILED));\n\n            markedAs.getItems().addAll(markAsClearedItem, markAsReconciledItem, markAsUnreconciledItem);\n\n            final MenuItem duplicateItem = new MenuItem(resources.getString(\"Menu.Duplicate.Name\"));\n            duplicateItem.setOnAction(event -> duplicateTransactions());\n\n            final MenuItem jumpItem = new MenuItem(resources.getString(\"Menu.Jump.Name\"));\n            jumpItem.setOnAction(event -> handleJumpAction());\n\n            final MenuItem deleteItem = new MenuItem(resources.getString(\"Menu.Delete.Name\"));\n            deleteItem.setOnAction(event -> deleteTransactions());\n\n            final MenuItem reminderItem = new MenuItem(resources.getString(\"Menu.NewReminder.Name\"));\n            reminderItem.setOnAction(event -> handleCreateNewReminder());\n\n            final MenuItem copyClipBoardItem = new MenuItem(resources.getString(\"Menu.CopyToClipboard.Name\"));\n            copyClipBoardItem.setOnAction(event -> handleCopyToClipboard());\n\n            rowMenu.getItems().addAll(markedAs, new SeparatorMenuItem(), duplicateItem, jumpItem,\n                    new SeparatorMenuItem(), deleteItem, new SeparatorMenuItem(), reminderItem, new SeparatorMenuItem(),\n                    copyClipBoardItem);\n\n            // only display context menu for non-null items:\n            row.contextMenuProperty().bind(\n                    Bindings.when(Bindings.isNotNull(row.itemProperty())).then(rowMenu).otherwise((ContextMenu) null));\n\n            // only display the tooltip if the selection size is greater than one\n            row.tooltipProperty().bind(Bindings.when(Bindings.greaterThan(selectionSize, 1))\n                                               .then(selectionSummaryTooltip).otherwise((Tooltip) null));\n\n            return row;\n        }\n    }\n\n    private class MessageBusHandler implements MessageListener {\n\n        /**\n         * Limits number of refresh and packTable calls while ensuring the most recent\n         * is executed.\n         */\n        private final ThreadPoolExecutor updateTableExecutor;\n\n        MessageBusHandler() {\n            updateTableExecutor = new ThreadPoolExecutor(1, 1, Long.MAX_VALUE, TimeUnit.DAYS,\n                    new ArrayBlockingQueue<>(1));\n\n            updateTableExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());\n        }\n\n        private void refreshTable() {\n            updateTableExecutor.execute(() -> {\n                tableView.refresh();\n                tableViewManager.packTable();\n            });\n        }\n\n        @Override\n        public void messagePosted(final Message event) {\n            final Account acc = RegisterTableController.this.account.getValue();\n\n            if (acc != null && event.getObject(MessageProperty.ACCOUNT).equals(acc)) {\n                switch (event.getEvent()) {\n                    case TRANSACTION_REMOVE:\n                        final Transaction removedTransaction = event.getObject(MessageProperty.TRANSACTION);\n\n                        // clear the selection if the transaction is currently selected\n                        if (tableView.getSelectionModel().getSelectedItems().contains(removedTransaction)) {\n                            JavaFXUtils.runLater(RegisterTableController.this::clearTableSelection);\n                        }\n\n                        /*\n                         * push removal to the end of the application thread to ensure the table\n                         * selection is cleared first to prevent an IndexOfOutBoundsException\n                         */\n                        JavaFXUtils.runLater(() -> observableTransactions.remove(removedTransaction));\n\n                        // this will force the running balance to recalculate\n                        refreshTable();\n\n                        break;\n                    case TRANSACTION_ADD:\n                        final Transaction addedTransaction = event.getObject(MessageProperty.TRANSACTION);\n\n                        JavaFXUtils.runLater(() -> {\n\n                            final int index = Collections.binarySearch(observableTransactions, addedTransaction,\n                                    tableView.getComparator());\n\n                            if (index < 0) {\n                                observableTransactions.add(-index - 1, addedTransaction);\n                            }\n\n                            // scroll to the new transaction\n                            JavaFXUtils.runLater(() -> scrollToTransaction(addedTransaction));\n\n                            // this will force the running balance to recalculate\n                            refreshTable();\n                        });\n\n                        break;\n                    default:\n                }\n\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/RegisterViewController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.prefs.Preferences;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.SplitPane;\nimport javafx.scene.control.TreeCell;\nimport javafx.scene.control.TreeView;\nimport javafx.scene.layout.StackPane;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.control.AbstractAccountTreeController;\nimport jgnash.uifx.report.ReportActions;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.AccountTypeFilter;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.accounts.StaticAccountsMethods;\nimport jgnash.util.DefaultDaemonThreadFactory;\n\n/**\n * Top level view for account registers.\n *\n * @author Craig Cavanaugh\n */\npublic class RegisterViewController {\n\n    private static final String DIVIDER_POSITION = \"DividerPosition\";\n\n    private static final String LAST_ACCOUNT = \"LastAccount\";\n\n    private static final double DEFAULT_DIVIDER_POSITION = 0.2;\n\n    @FXML\n    public SplitPane splitPane;\n\n    @FXML\n    private Button reconcileButton;\n\n    @FXML\n    private Button filterButton;\n\n    @FXML\n    private Button zoomButton;\n\n    @FXML\n    private Button packColumnsButton;\n\n    @FXML\n    private TreeView<Account> treeView;\n\n    @FXML\n    private StackPane registerPane;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final static ExecutorService executorService =\n            Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory(\"Register View Controller Executor\"));\n\n    private final Preferences preferences = Preferences.userNodeForPackage(RegisterViewController.class);\n\n    private final AccountTypeFilter typeFilter = new AccountTypeFilter(Preferences.userNodeForPackage(getClass()));\n\n    private RegisterPaneController registerPaneController;\n\n    private final AbstractAccountTreeController accountTreeController = new AbstractAccountTreeController() {\n        @Override\n        protected TreeView<Account> getTreeView() {\n            return treeView;\n        }\n\n        @Override\n        protected boolean isAccountVisible(Account account) {\n            return typeFilter.isAccountVisible(account);\n        }\n\n        @Override\n        protected boolean isAccountSelectable(final Account account) {\n            return !account.isPlaceHolder();\n        }\n\n        @Override\n        public void initialize() {\n            super.initialize();\n\n            // Install a cell that disables selection if the account is a placeholder\n            getTreeView().setCellFactory(param -> new DisabledTreeCell());\n        }\n    };\n\n    @FXML\n    private void initialize() {\n        accountTreeController.initialize(); // must initialize the account controller\n\n        // Filter changes should force a reload of the tree\n        typeFilter.addListener(observable -> accountTreeController.reload());\n\n        // Remember the last divider location\n        splitPane.getDividers().get(0).positionProperty().addListener((observable, oldValue, newValue)\n                -> executorService.submit(() -> preferences.putDouble(DIVIDER_POSITION, (Double) newValue)));\n\n        // Remember the last account selection\n        accountTreeController.getSelectedAccountProperty().addListener((observable, oldValue, newValue) -> {\n            executorService.submit(() -> preferences.put(LAST_ACCOUNT, newValue.getUuid().toString()));\n            showAccount();\n        });\n\n        // Restore divider location\n        splitPane.setDividerPosition(0, preferences.getDouble(DIVIDER_POSITION, DEFAULT_DIVIDER_POSITION));\n\n        restoreLastSelectedAccount();\n    }\n\n    private void showAccount() {\n        if (registerPaneController != null) {\n            registerPaneController.accountProperty().unbind();\n            registerPane.getChildren().clear();\n        }\n\n        final Account account = accountTreeController.getSelectedAccountProperty().get();\n        final String formResource;\n\n        if (account.isLocked()) {\n            if (account.memberOf(AccountGroup.INVEST)) {\n                formResource = \"LockedInvestmentRegisterPane.fxml\";\n            } else {\n                formResource = \"LockedBasicRegisterPane.fxml\";\n            }\n        } else {\n            if (account.memberOf(AccountGroup.INVEST)) {\n                formResource = \"InvestmentRegisterPane.fxml\";\n            } else if (account.getAccountType() == AccountType.LIABILITY) {\n                formResource = \"LiabilityRegisterPane.fxml\";\n            } else {\n                formResource = \"BasicRegisterPane.fxml\";\n            }\n        }\n\n        registerPaneController = FXMLUtils.loadFXML(scene -> registerPane.getChildren().add(scene), formResource,\n                resources);\n\n        // Push the account to the controller at the end of the application thread\n        JavaFXUtils.runLater(() -> registerPaneController.accountProperty()\n                .set(accountTreeController.getSelectedAccountProperty().get()));\n    }\n\n    private void restoreLastSelectedAccount() {\n        executorService.submit(() -> {\n            final String lastAccountUuid = preferences.get(LAST_ACCOUNT, \"\");\n            if (!lastAccountUuid.isEmpty()) {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                if (engine != null) {\n                    final Account account = engine.getAccountByUuid(UUID.fromString(lastAccountUuid));\n                    if (account != null) {\n                        accountTreeController.setSelectedAccount(account);\n                    }\n                }\n            }\n        });\n    }\n\n    @FXML\n    private void handleFilterAccountAction() {\n        StaticAccountsMethods.showAccountFilterDialog(typeFilter);\n    }\n\n    @FXML\n    private void handleZoomAction() {\n        if (accountTreeController.getSelectedAccountProperty().get() != null) {\n            RegisterStage.getRegisterStage(accountTreeController.getSelectedAccountProperty().get()).show();\n        }\n    }\n\n    @FXML\n    private void handleReconcileAction() {\n        RegisterActions.reconcileAccountAction(accountTreeController.getSelectedAccountProperty().get());\n    }\n\n    @FXML\n    private void handleAccountExport() {\n        RegisterTableController registerTableController = registerPaneController.registerTableController.get();\n\n        final Account account = registerPaneController.accountProperty().get();\n\n        if (account != null && account.getTransactionCount() > 1) {\n            LocalDate startDate = account.getTransactionAt(0).getLocalDate();\n            LocalDate endDate = account.getTransactionAt(account.getTransactionCount() - 1).getLocalDate();\n\n            final List<Transaction> selected = new ArrayList<>(registerTableController.getSelectedTransactions());\n\n            if (selected.size() > 1) {\n                Collections.sort(selected);\n                startDate = selected.get(0).getLocalDate();\n                endDate = selected.get(selected.size() - 1).getLocalDate();\n            }\n\n            RegisterActions.exportTransactions(account, startDate, endDate);\n        }\n    }\n\n    @FXML\n    private void handleAccountReport() {\n        ReportActions.displayAccountRegisterReport(registerPaneController.accountProperty().get());\n    }\n\n    @FXML\n    private void handleTableColumnPack() {\n        registerPaneController.registerTableController.get().manuallyPackTable();\n    }\n\n    private static final class DisabledTreeCell extends TreeCell<Account> {\n        @Override\n        public void updateItem(final Account account, final boolean empty) {\n            super.updateItem(account, empty);   // required\n\n            if (!empty && account != null) {\n                setText(account.getName());\n\n                if (account.isPlaceHolder()) {\n                    setId(StyleClass.DISABLED_CELL_ID);\n                } else {\n                    setId(StyleClass.ENABLED_CELL_ID);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/ReinvestDividendSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.List;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.AbstractInvestmentTransactionEntry;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionEntryReinvestDivX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Reinvested dividends.\n *\n * @author Craig Cavanaugh\n */\npublic class ReinvestDividendSlipController extends AbstractPriceQtyInvSlipController {\n\n    @FXML\n    private GainLossPane gainLossPane;\n\n    @FXML\n    private FeePane feePane;\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        gainLossPane.accountProperty().bind(accountProperty());\n        feePane.accountProperty().bind(accountProperty());\n\n        accountProperty().addListener((observable, oldValue, newValue) -> clearForm());\n\n        final ChangeListener<BigDecimal> changeListener = (observable, oldValue, newValue) -> updateTotalField();\n\n        quantityField.decimalProperty().addListener(changeListener);\n        priceField.decimalProperty().addListener(changeListener);\n        gainLossPane.decimalProperty().addListener(changeListener);\n        feePane.decimalProperty().addListener(changeListener);\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (!(transaction instanceof InvestmentTransaction)\n                || transaction.getTransactionType() != TransactionType.REINVESTDIV) {\n            throw new IllegalArgumentException(resources.getString(\"Message.Error.InvalidTransactionType\"));\n        }\n\n        clearForm();\n\n        datePicker.setValue(transaction.getLocalDate());\n        numberComboBox.setValue(transaction.getNumber());\n\n        feePane.setTransactionEntries(((InvestmentTransaction) transaction).getInvestmentFeeEntries());\n        gainLossPane.setTransactionEntries(((InvestmentTransaction) transaction).getInvestmentGainLossEntries());\n\n        transaction.getTransactionEntries().stream().filter(TransactionEntryReinvestDivX.class::isInstance).forEach(e -> {\n            final AbstractInvestmentTransactionEntry entry = (AbstractInvestmentTransactionEntry) e;\n\n            memoTextField.setText(e.getMemo());\n            priceField.setDecimal(entry.getPrice());\n            quantityField.setDecimal(entry.getQuantity());\n            securityComboBox.setSecurityNode(entry.getSecurityNode());\n        });\n\n        tagPane.setSelectedTags(transaction.getTags(TransactionEntryReinvestDivX.class));\n\n        modTrans = transaction;\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        setReconciledState(transaction.getReconciled(accountProperty().get()));\n    }\n\n    private void updateTotalField() {\n        totalField.setDecimal(quantityField.getDecimal().multiply(priceField.getDecimal()).subtract(feePane.getDecimal()));\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        final List<TransactionEntry> gains = gainLossPane.getTransactions();\n        final List<TransactionEntry> fees = feePane.getTransactions();\n\n        final Transaction transaction = TransactionFactory.generateReinvestDividendXTransaction(accountProperty().get(),\n                securityComboBox.getValue(), priceField.getDecimal(), quantityField.getDecimal(), datePicker.getValue(),\n                memoTextField.getText(), fees, gains);\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(TransactionEntryReinvestDivX.class, tagPane.getSelectedTags());\n\n        return attachmentPane.buildTransaction(transaction);\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        feePane.clearForm();\n        gainLossPane.clearForm();\n\n        attachmentPane.clear();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/ReturnOfCapitalSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\n\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntryRocX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Return of Capital entry controller.\n *\n * @author Craig Cavanaugh\n */\npublic class ReturnOfCapitalSlipController extends AbstractInvIncomeSlipController {\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        BigDecimal incomeExchangedAmount = decimalTextField.getDecimal().negate();\n\n        BigDecimal accountExchangedAmount = decimalTextField.getDecimal();\n\n        if (!incomeExchangePane.getSelectedAccount().getCurrencyNode().equals(accountProperty().get().getCurrencyNode())) {\n            incomeExchangedAmount = incomeExchangePane.exchangeAmountProperty().get().negate();\n        }\n\n        if (!accountExchangePane.getSelectedAccount().getCurrencyNode().equals(accountProperty().get().getCurrencyNode())) {\n            accountExchangedAmount = accountExchangePane.exchangeAmountProperty().get();\n        }\n\n        final Transaction transaction = TransactionFactory.generateRocXTransaction(incomeExchangePane.getSelectedAccount(),\n                accountProperty().get(), accountExchangePane.getSelectedAccount(), securityComboBox.getValue(),\n                decimalTextField.getDecimal(), incomeExchangedAmount, accountExchangedAmount, datePicker.getValue(),\n                memoTextField.getText());\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(TransactionEntryRocX.class, tagPane.getSelectedTags());\n\n        return transaction;\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull Transaction transaction) {\n        super.modifyTransaction(transaction);\n\n        tagPane.setSelectedTags(transaction.getTags(TransactionEntryRocX.class));\n    }\n\n    @Override\n    TransactionType getTransactionType() {\n        return TransactionType.RETURNOFCAPITAL;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SellShareSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.AbstractInvestmentTransactionEntry;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionEntrySellX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Credits and Debits.\n *\n * @author Craig Cavanaugh\n */\npublic class SellShareSlipController extends AbstractPriceQtyInvSlipController {\n\n    @FXML\n    private GainLossPane gainLossPane;\n\n    @FXML\n    private FeePane feePane;\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    @FXML\n    private AccountExchangePane accountExchangePane;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        // don't filter the base account for investment transactions\n        accountExchangePane.filterBaseAccountProperty().set(false);\n\n        // Bind necessary properties to the exchange panel\n        accountExchangePane.baseAccountProperty().bind(accountProperty());\n        accountExchangePane.amountProperty().bindBidirectional(totalField.decimalProperty());\n        accountExchangePane.amountEditableProperty().bind(totalField.editableProperty());\n\n        gainLossPane.accountProperty().bind(accountProperty());\n        feePane.accountProperty().bind(accountProperty());\n\n        accountProperty().addListener((observable, oldValue, newValue) -> clearForm());\n\n        final ChangeListener<BigDecimal> changeListener = (observable, oldValue, newValue) -> updateTotalField();\n\n        quantityField.decimalProperty().addListener(changeListener);\n        priceField.decimalProperty().addListener(changeListener);\n        gainLossPane.decimalProperty().addListener(changeListener);\n        feePane.decimalProperty().addListener(changeListener);\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (!(transaction instanceof InvestmentTransaction)\n                || transaction.getTransactionType() != TransactionType.SELLSHARE) {\n            throw new IllegalArgumentException(resources.getString(\"Message.Error.InvalidTransactionType\"));\n        }\n\n        clearForm();\n\n        datePicker.setValue(transaction.getLocalDate());\n        numberComboBox.setValue(transaction.getNumber());\n\n        feePane.setTransactionEntries(((InvestmentTransaction) transaction).getInvestmentFeeEntries());\n        gainLossPane.setTransactionEntries(((InvestmentTransaction) transaction).getInvestmentGainLossEntries());\n\n        transaction.getTransactionEntries().stream().filter(TransactionEntrySellX.class::isInstance).forEach(e -> {\n            final AbstractInvestmentTransactionEntry entry = (AbstractInvestmentTransactionEntry) e;\n\n            memoTextField.setText(e.getMemo());\n            priceField.setDecimal(entry.getPrice());\n            quantityField.setDecimal(entry.getQuantity());\n            securityComboBox.setSecurityNode(entry.getSecurityNode());\n\n            if (entry.getCreditAccount().equals(accountProperty().get())) {\n                accountExchangePane.setSelectedAccount(entry.getDebitAccount());\n                accountExchangePane.setExchangedAmount(entry.getDebitAmount().abs());\n            } else {\n                //accountExchangePane.setSelectedAccount(entry.getCreditAccount());\n                //accountExchangePane.setExchangedAmount(entry.getCreditAmount());\n                Logger.getLogger(SellShareSlipController.class.getName()).warning(\"was not expected\");\n            }\n        });\n\n        tagPane.setSelectedTags(transaction.getTags(TransactionEntrySellX.class));\n\n        modTrans = transaction;\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        setReconciledState(transaction.getReconciled(accountProperty().get()));\n    }\n\n    private void updateTotalField() {\n        totalField.setDecimal(quantityField.getDecimal().multiply(priceField.getDecimal()).add(gainLossPane.getDecimal()));\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        final BigDecimal exchangeRate = accountExchangePane.getExchangeRate();\n        final List<TransactionEntry> gains = gainLossPane.getTransactions();\n        final List<TransactionEntry> fees = feePane.getTransactions();\n\n        final Transaction transaction = TransactionFactory.generateSellXTransaction(accountExchangePane.getSelectedAccount(),\n                accountProperty().get(), securityComboBox.getValue(), priceField.getDecimal(),\n                quantityField.getDecimal(), exchangeRate, datePicker.getValue(), memoTextField.getText(), fees, gains);\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(TransactionEntrySellX.class, tagPane.getSelectedTags());\n\n        return attachmentPane.buildTransaction(transaction);\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        feePane.clearForm();\n        gainLossPane.clearForm();\n\n        attachmentPane.clear();\n        accountExchangePane.setExchangedAmount(null);\n        accountExchangePane.setSelectedAccount(accountProperty().get());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/Slip.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.time.LocalDate;\nimport java.time.Month;\n\nimport javafx.beans.binding.DoubleBinding;\nimport javafx.beans.property.DoubleProperty;\nimport javafx.beans.property.SimpleDoubleProperty;\n\nimport jgnash.engine.Transaction;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction slip interface.\n *\n * @author Craig Cavanaugh.\n */\ninterface Slip extends BaseSlip {\n\n    /**\n     * Manages the date column width to compensate for date format and font scale\n     */\n    DoubleProperty dateColumnWidth = new SimpleDoubleProperty();\n\n    int ICON_BORDER_WIDTH = 43;\n\n    /**\n     * Loads a {@code Transaction} into the form and sets it up for modification.\n     *\n     * @param transaction {@code Transaction} to load for modification\n     */\n    void modifyTransaction(@NotNull final Transaction transaction);\n\n    /**\n     * Builds and returns a new {@code Transaction} based on form contents.\n     *\n     * @return new {@code Transaction} instance\n     */\n    @NotNull\n    Transaction buildTransaction();\n\n    default DoubleBinding getDateColumnWidth(final String style) {\n\n        double textWidth = Math.ceil(JavaFXUtils.getDisplayedTextWidth(DateUtils.getShortDateManualEntryFormatter()\n                .format(LocalDate.of(2028, Month.DECEMBER, 28)), style)\n                / ThemeManager.fontScaleProperty().get());\n\n        return ThemeManager.fontScaleProperty().multiply(textWidth).add(ICON_BORDER_WIDTH);\n    }\n\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.beans.property.BooleanProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleListProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.collections.FXCollections;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.Tag;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.util.LogUtil;\nimport jgnash.util.NotNull;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.logging.Logger;\n\n/**\n * Transaction Entry Controller for Credits and Debits.\n *\n * @author Craig Cavanaugh\n */\npublic class SlipController extends AbstractSlipController {\n\n    private final SimpleListProperty<TransactionEntry> transactionEntries =\n            new SimpleListProperty<>(FXCollections.observableArrayList());\n\n    private final SimpleObjectProperty<TransactionEntry> modEntry = new SimpleObjectProperty<>();\n\n    private final BooleanProperty concatenated = new SimpleBooleanProperty();\n\n    @FXML\n    protected Button cancelButton;\n\n    @FXML\n    protected Button enterButton;\n\n    @FXML\n    private Button splitsButton;\n\n    @FXML\n    private AccountExchangePane accountExchangePane;\n\n    private SlipType slipType;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        validFormProperty.bind(amountField.validDecimalProperty()\n                .and(Bindings.isNotNull(accountExchangePane.selectedAccountProperty()))\n        );\n\n        // Bind necessary properties to the exchange panel\n        accountExchangePane.baseAccountProperty().bind(accountProperty());\n        accountExchangePane.amountProperty().bindBidirectional(amountField.decimalProperty());\n        accountExchangePane.amountEditableProperty().bind(amountField.editableProperty());\n\n        // Bind the enter button, effectively negates the need for validation\n        if (enterButton != null) {  // enter button may not have been initialized if used for an investment slip\n            enterButton.disableProperty().bind(validFormProperty.not());\n        }\n\n        amountField.editableProperty().bind(transactionEntries.emptyProperty());\n        accountExchangePane.disableProperty().bind(transactionEntries.emptyProperty().not()\n                .or(modEntry.isNotNull()));\n\n        memoTextField.disableProperty().bind(concatenated);\n    }\n\n    void setSlipType(final SlipType slipType) {\n        this.slipType = slipType;\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (transaction.areAccountsLocked()) {\n            clearForm();\n            StaticUIMethods.displayError(resources.getString(\"Message.TransactionModifyLocked\"));\n            return;\n        }\n\n        newTransaction(transaction); // load the form\n\n        modTrans = transaction; // save reference to old transaction\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        tagPane.setSelectedTags(transaction.getTags(account.get()));\n\n        // Set state of memo concatenation\n        concatenated.setValue(modTrans.isMemoConcatenated());\n\n        if (!canModifyTransaction(transaction) && transaction.getTransactionType() == TransactionType.SPLITENTRY) {\n            for (final TransactionEntry entry : transaction.getTransactionEntries()) {\n                if (entry.getCreditAccount().equals(accountProperty().get()) || entry.getDebitAccount().equals(accountProperty().get())) {\n                    modEntry.setValue(entry);\n\n                    concatenated.setValue(false); // override to allow editing the entry\n                    break;\n                }\n            }\n\n            if (modEntry.get() == null) {\n                Logger logger = Logger.getLogger(SlipController.class.getName());\n                logger.warning(\"Was not able to modify the transaction\");\n            }\n        }\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n\n        Transaction transaction;\n\n        final LocalDate date = datePicker.getValue();\n\n        if (!transactionEntries.isEmpty()) { // build a split transaction\n            transaction = new Transaction();\n\n            transaction.setDate(date);\n            transaction.setNumber(numberComboBox.getValue());\n            transaction.setMemo(Options.concatenateMemosProperty().get() ? Transaction.CONCATENATE\n                    : memoTextField.getText());\n            transaction.setPayee(payeeTextField.getText());\n\n            transaction.addTransactionEntries(transactionEntries);\n        } else {  // double entry transaction\n            final int signum = amountField.getDecimal().signum();\n\n            final Account selectedAccount;\n\n            if (modTrans != null && modTrans.areAccountsHidden()) {\n                selectedAccount = getOppositeSideAccount(modTrans);\n            } else {\n                selectedAccount = accountExchangePane.getSelectedAccount();\n            }\n\n            if (slipType == SlipType.DECREASE && signum >= 0 || slipType == SlipType.INCREASE && signum == -1) {\n                if (hasEqualCurrencies()) {\n                    transaction = TransactionFactory.generateDoubleEntryTransaction(selectedAccount,\n                            account.get(), amountField.getDecimal().abs(), date, memoTextField.getText(),\n                            payeeTextField.getText(), numberComboBox.getValue());\n                } else {\n                    transaction = TransactionFactory.generateDoubleEntryTransaction(selectedAccount,\n                            account.get(), accountExchangePane.exchangeAmountProperty().get().abs(),\n                            amountField.getDecimal().abs().negate(), date, memoTextField.getText(),\n                            payeeTextField.getText(), numberComboBox.getValue());\n                }\n            } else {\n                if (hasEqualCurrencies()) {\n                    transaction = TransactionFactory.generateDoubleEntryTransaction(account.get(),\n                            selectedAccount, amountField.getDecimal().abs(), date, memoTextField.getText(),\n                            payeeTextField.getText(), numberComboBox.getValue());\n                } else {\n                    transaction = TransactionFactory.generateDoubleEntryTransaction(account.get(),\n                            selectedAccount, amountField.getDecimal().abs(),\n                            accountExchangePane.exchangeAmountProperty().get().abs().negate(), date,\n                            memoTextField.getText(), payeeTextField.getText(), numberComboBox.getValue());\n                }\n            }\n\n            transaction.setTags(tagPane.getSelectedTags());\n        }\n\n        ReconcileManager.reconcileTransaction(account.get(), transaction, getReconciledState());\n\n        return transaction;\n    }\n\n    private TransactionEntry buildTransactionEntry() {\n        final TransactionEntry entry = new TransactionEntry();\n        entry.setMemo(memoTextField.getText());\n\n        final int signum = amountField.getDecimal().signum();\n\n        if (slipType == SlipType.DECREASE && signum >= 0 || slipType == SlipType.INCREASE && signum < 0) {\n            entry.setCreditAccount(accountExchangePane.getSelectedAccount());\n            entry.setDebitAccount(account.get());\n\n            if (hasEqualCurrencies()) {\n                entry.setAmount(amountField.getDecimal().abs());\n            } else {\n                entry.setDebitAmount(accountExchangePane.exchangeAmountProperty().get().abs().negate());\n            }\n        } else {\n            entry.setCreditAccount(account.get());\n            entry.setDebitAccount(accountExchangePane.getSelectedAccount());\n\n            if (hasEqualCurrencies()) {\n                entry.setAmount(amountField.getDecimal().abs());\n            } else {\n                entry.setCreditAmount(accountExchangePane.exchangeAmountProperty().get().abs());\n            }\n        }\n\n        entry.setTags(tagPane.getSelectedTags());\n\n        entry.setReconciled(account.get(), getReconciledState());\n\n        return entry;\n    }\n\n    private boolean hasEqualCurrencies() {\n        return account.get().getCurrencyNode().equals(accountExchangePane.getSelectedAccount().getCurrencyNode());\n    }\n\n    private Account getOppositeSideAccount(final Transaction t) {\n        TransactionEntry entry = t.getTransactionEntries().get(0);\n\n        if (entry.getCreditAccount().equals(account.get())) {\n            return entry.getDebitAccount();\n        }\n        return entry.getCreditAccount();\n    }\n\n    private void newTransaction(final Transaction t) {\n        clearForm();\n\n        amountField.setDecimal(t.getAmount(accountProperty().get()).abs());\n\n        // Must consider if this is a concatenated memo to set the field correctly\n        memoTextField.setText(t.isMemoConcatenated() ? t.getMemo() : t.getTransactionMemo());\n\n        payeeTextField.setText(t.getPayee());\n        numberComboBox.setValue(t.getNumber());\n        datePicker.setValue(t.getLocalDate());\n        setReconciledState(t.getReconciled(accountProperty().get()));\n\n        if (t.getTransactionType() == TransactionType.SPLITENTRY) {\n\n            if (canModifyTransaction(t)) { // split common account is the same as the base account\n\n                //  clone the splits for modification\n                transactionEntries.clear();\n\n                for (final TransactionEntry entry : t.getTransactionEntries()) {\n                    try {\n                        transactionEntries.add((TransactionEntry) entry.clone());\n                    } catch (final CloneNotSupportedException e) {\n                        LogUtil.logSevere(SlipController.class, e);\n                    }\n                }\n\n                tagPane.setSelectedTags(t.getTags());\n                tagPane.setReadOnly(true);\n\n                amountField.setDecimal(t.getAmount(accountProperty().get()).abs());\n            } else { // not the same common account, can only modify the entry\n                splitsButton.setDisable(true);\n                payeeTextField.setEditable(false);\n                numberComboBox.setDisable(true);\n                datePicker.setEditable(false);\n\n                memoTextField.setText(t.getMemo(account.get()));   // Override\n\n                amountField.setDecimal(t.getAmount(accountProperty().get()).abs());\n\n                for (final TransactionEntry entry : t.getTransactionEntries()) {\n                    if (entry.getCreditAccount() == accountProperty().get()) {\n                        accountExchangePane.setExchangedAmount(entry.getDebitAmount().abs());\n                        tagPane.setSelectedTags(entry.getTags());\n                        break;\n                    } else if (entry.getDebitAccount() == accountProperty().get()) {\n                        accountExchangePane.setExchangedAmount(entry.getCreditAmount());\n                        tagPane.setSelectedTags(entry.getTags());\n                        break;\n                    }\n                }\n\n                tagPane.setSelectedTags(t.getTags(account.get()));\n                tagPane.setReadOnly(false);\n            }\n        } else if (t instanceof InvestmentTransaction) {\n            Logger logger = Logger.getLogger(SlipController.class.getName());\n            logger.warning(\"unsupported transaction type\");\n\n        } else { // DoubleEntryTransaction\n            datePicker.setEditable(true);\n            tagPane.setSelectedTags(t.getTags(account.get()));\n            tagPane.setReadOnly(false);\n        }\n\n        // setup the accountCombo correctly\n        if (t.getTransactionType() == TransactionType.DOUBLEENTRY) {\n            TransactionEntry entry = t.getTransactionEntries().get(0);\n\n            if (slipType == SlipType.DECREASE) {\n                accountExchangePane.setSelectedAccount(entry.getCreditAccount());\n                accountExchangePane.setExchangedAmount(entry.getCreditAmount());\n            } else {\n                accountExchangePane.setSelectedAccount(entry.getDebitAccount());\n                accountExchangePane.setExchangedAmount(entry.getDebitAmount().abs());\n            }\n        }\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        // Not yet a split transaction\n        concatenated.set(false);\n\n        transactionEntries.clear();   // clear an old transaction entries\n\n        modEntry.set(null);\n\n        accountExchangePane.setExchangedAmount(null);\n\n        splitsButton.setDisable(false);\n    }\n\n    @Override\n    boolean canModifyTransaction(final Transaction t) {\n        boolean result = false; // fail unless proven otherwise\n\n        switch (t.getTransactionType()) {\n            case DOUBLEENTRY:\n                final TransactionEntry entry = t.getTransactionEntries().get(0);\n\n                // Prevent loading of a single entry scenario into the form.  The user may not detect it\n                // and the engine will throw an exception if undetected.\n                if (slipType == SlipType.DECREASE) {\n                    if (!accountProperty().get().equals(entry.getCreditAccount())) {\n                        result = true;\n                    }\n                } else {\n                    if (!accountProperty().get().equals(entry.getDebitAccount())) {\n                        result = true;\n                    }\n                }\n\n                break;\n            case SPLITENTRY:\n                if (t.getCommonAccount().equals(account.get())) {\n                    result = true;\n                }\n                break;\n            default:\n                break;\n        }\n\n        return result;\n    }\n\n    @Override\n    public void handleEnterAction() {\n        if (modEntry.get() != null && modTrans != null) {\n            try {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                // clone the transaction\n                final Transaction t = (Transaction) modTrans.clone();\n\n                // remove the modifying entry from the clone\n                t.removeTransactionEntry(modEntry.get());\n\n                // generate new TransactionEntry\n                final TransactionEntry e = buildTransactionEntry();\n\n                // add it to the clone\n                t.addTransactionEntry(e);\n\n                ReconcileManager.reconcileTransaction(account.get(), t, getReconciledState());\n\n                if (engine.removeTransaction(modTrans)) {\n                    engine.addTransaction(t);\n                }\n\n                clearForm();\n                focusFirstComponent();\n            } catch (CloneNotSupportedException e) {\n                LogUtil.logSevere(SlipController.class, e);\n            }\n        } else {\n            super.handleEnterAction();\n        }\n    }\n\n    @FXML\n    private void splitsAction() {\n        final SplitTransactionDialog splitsDialog = new SplitTransactionDialog();\n        splitsDialog.accountProperty().setValue(accountProperty().get());\n        splitsDialog.getTransactionEntries().setAll(transactionEntries);\n\n        final boolean wasSplit = transactionEntries.get().size() > 0;\n\n        // Show the dialog and process the transactions when it closes\n        splitsDialog.show(slipType, () -> {\n            transactionEntries.setAll(splitsDialog.getTransactionEntries());\n\n            if (transactionEntries.get().size() > 0) {\n                amountField.setDecimal(splitsDialog.getBalance().abs());\n            } else if (wasSplit) {\n                amountField.setDecimal(BigDecimal.ZERO);    // spits were cleared out\n            }\n\n            // If valid splits exist and the user has requested concatenation, show a preview of what will happen\n            concatenated.setValue(Options.concatenateMemosProperty().get()\n                    && !transactionEntries.isEmpty());\n\n            if (concatenated.get()) {\n                memoTextField.setText(Transaction.getMemo(transactionEntries));\n            }\n\n            if (transactionEntries.get().size() > 0) {  // process the tags from the split transaction\n                final Set<Tag> tags = new HashSet<>();\n\n                for (final TransactionEntry entry : transactionEntries.get()) {\n                    tags.addAll(entry.getTags());\n                }\n\n                tagPane.setSelectedTags(tags);\n                tagPane.refreshTagView();\n            } else if (wasSplit) {  // clear out all the prior split entries\n                tagPane.clearSelectedTags();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SlipControllerContainer.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.scene.layout.Pane;\n\n/**\n * Utility class to hold the controller, slip, and slip description.\n *\n * @author Craig Cavanaugh\n */\nclass SlipControllerContainer {\n    private final String description;\n    private final Slip controller;\n    private final Pane pane;\n\n    SlipControllerContainer(final String description, final Slip controller, final Pane pane) {\n        this.description = description;\n        this.controller = controller;\n        this.pane = pane;\n    }\n\n    public Slip getController() {\n        return controller;\n    }\n\n    public Pane getPane() {\n        return pane;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SlipType.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\n/**\n * Panel type enumeration.\n *\n * @author Craig Cavanaugh\n */\nenum SlipType {\n    INCREASE, DECREASE\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SplitMergeSharesSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\n\nimport javafx.beans.value.ChangeListener;\nimport javafx.fxml.FXML;\n\nimport jgnash.engine.InvestmentTransaction;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntryMergeX;\nimport jgnash.engine.TransactionEntrySplitX;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Split and Merges shares.\n *\n * @author Craig Cavanaugh\n */\npublic class SplitMergeSharesSlipController extends AbstractPriceQtyInvSlipController {\n\n    @FXML\n    protected AttachmentPane attachmentPane;\n\n    private TransactionType tranType = TransactionType.SPLITSHARE;\n\n    @FXML\n    public void initialize() {\n        super.initialize();\n\n        accountProperty().addListener((observable, oldValue, newValue) -> clearForm());\n\n        final ChangeListener<BigDecimal> changeListener = (observable, oldValue, newValue) -> updateTotalField();\n\n        quantityField.decimalProperty().addListener(changeListener);\n        priceField.decimalProperty().addListener(changeListener);\n    }\n\n    void setTransactionType(final TransactionType tranType) {\n        this.tranType = tranType;\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        if (!(transaction instanceof InvestmentTransaction) ||\n                !(transaction.getTransactionType() == TransactionType.SPLITSHARE ||\n                transaction.getTransactionType() == TransactionType.MERGESHARE)) {\n            throw new IllegalArgumentException(resources.getString(\"Message.Error.InvalidTransactionType\"));\n        }\n\n        clearForm();\n\n        datePicker.setValue(transaction.getLocalDate());\n        memoTextField.setText(transaction.getTransactionMemo());\n        numberComboBox.setValue(transaction.getNumber());\n        priceField.setDecimal(((InvestmentTransaction)transaction).getPrice());\n        quantityField.setDecimal(((InvestmentTransaction)transaction).getQuantity());\n        securityComboBox.setSecurityNode(((InvestmentTransaction)transaction).getSecurityNode());\n\n        tagPane.setSelectedTags(transaction.getTags(tranType == TransactionType.SPLITSHARE\n                                                            ? TransactionEntrySplitX.class\n                                                            : TransactionEntryMergeX.class));\n\n        modTrans = transaction;\n        modTrans = attachmentPane.modifyTransaction(modTrans);\n\n        setReconciledState(transaction.getReconciled(accountProperty().get()));\n    }\n\n    private void updateTotalField() {\n        totalField.setDecimal(quantityField.getDecimal().multiply(priceField.getDecimal()));\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n\n        final Transaction transaction;\n\n        if (tranType == TransactionType.SPLITSHARE) {\n            transaction = TransactionFactory.generateSplitXTransaction(accountProperty().get(), securityComboBox.getValue(),\n                    priceField.getDecimal(), quantityField.getDecimal(), datePicker.getValue(), memoTextField.getText());\n        } else {\n            transaction = TransactionFactory.generateMergeXTransaction(accountProperty().get(), securityComboBox.getValue(),\n                    priceField.getDecimal(), quantityField.getDecimal(), datePicker.getValue(), memoTextField.getText());\n        }\n\n        transaction.setNumber(numberComboBox.getValue());\n\n        transaction.setTags(tranType == TransactionType.SPLITSHARE\n                                    ? TransactionEntrySplitX.class\n                                    : TransactionEntryMergeX.class, tagPane.getSelectedTags());\n\n        return attachmentPane.buildTransaction(transaction);\n    }\n\n    @Override\n    public void clearForm() {\n        super.clearForm();\n\n        attachmentPane.clear();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SplitTransactionDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.stage.WindowEvent;\n\nimport jgnash.engine.TransactionEntry;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.util.NotNull;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Split Transaction entry dialog.\n *\n * @author Craig Cavanaugh\n */\nclass SplitTransactionDialog extends AbstractTransactionEntryDialog {\n\n    private static final String PREF_NODE_USER_ROOT = \"/jgnash/uifx/views/register/splits\";\n\n    @FXML\n    private TabPane tabPane;\n\n    @FXML\n    private CheckBox concatenateMemosCheckBox;\n\n    private Tab creditTab;\n\n    private Tab debitTab;\n\n    SplitTransactionDialog() {\n        FXMLUtils.loadFXML(this, \"SplitTransactionDialog.fxml\", ResourceUtils.getBundle());\n        setTitle(ResourceUtils.getString(\"Title.SpitTran\"));\n    }\n\n    @Override\n    String getPrefNode() {\n        return PREF_NODE_USER_ROOT;\n    }\n\n    @Override\n    void newAction() {\n        ((SplitTransactionSlipController) creditTab.getUserData()).clearForm();\n        ((SplitTransactionSlipController) debitTab.getUserData()).clearForm();\n        tableView.getSelectionModel().clearSelection();\n    }\n\n    @Override\n    void deleteAction() {\n        final TransactionEntry entry = tableView.getSelectionModel().getSelectedItem();\n        if (entry != null) {\n            tableView.getSelectionModel().clearSelection();\n            ((SplitTransactionSlipController) tabPane.getSelectionModel().getSelectedItem().getUserData()).clearForm();\n            getTransactionEntries().remove(entry);\n        }\n    }\n\n\n    @Override\n    void modifyTransactionEntry(@NotNull final TransactionEntry transactionEntry) {\n        if (transactionEntry.getCreditAccount() == accountProperty().get()) { // this is a credit\n            tabPane.getSelectionModel().select(creditTab);\n            ((SplitTransactionSlipController) creditTab.getUserData()).modifyTransactionEntry(transactionEntry);\n        } else {\n            tabPane.getSelectionModel().select(debitTab);\n            ((SplitTransactionSlipController) debitTab.getUserData()).modifyTransactionEntry(transactionEntry);\n        }\n    }\n\n    @Override\n    void initForm() {\n        concatenateMemosCheckBox.selectedProperty().bindBidirectional(Options.concatenateMemosProperty());\n\n        final String[] tabNames = RegisterFactory.getCreditDebitTabNames(accountProperty().get().getAccountType());\n\n        creditTab = new Tab(tabNames[0]);\n\n        final SplitTransactionSlipController creditController = FXMLUtils.loadFXML(o -> creditTab.setContent(o),\n                \"SplitTransactionSlip.fxml\", resources);\n\n        creditTab.setUserData(creditController);\n\n        creditController.setSlipType(SlipType.INCREASE);\n        creditController.accountProperty().set(accountProperty().getValue());\n        creditController.transactionEntryListProperty().set(getTransactionEntries());\n        creditController.comparatorProperty().bind(tableView.comparatorProperty());\n\n        debitTab = new Tab(tabNames[1]);\n\n        final SplitTransactionSlipController debitController = FXMLUtils.loadFXML(o -> debitTab.setContent(o),\n                \"SplitTransactionSlip.fxml\", resources);\n\n        debitTab.setUserData(debitController);\n\n        debitController.setSlipType(SlipType.DECREASE);\n        debitController.accountProperty().set(accountProperty().getValue());\n        debitController.transactionEntryListProperty().set(getTransactionEntries());\n        debitController.comparatorProperty().bind(tableView.comparatorProperty());\n\n        tabPane.getTabs().addAll(creditTab, debitTab);\n\n        // Install a listener to unbind from the Options to prevent leaks\n        if (getScene() != null) {\n            getScene().windowProperty().get().addEventHandler(WindowEvent.WINDOW_HIDING,\n                    event -> concatenateMemosCheckBox.selectedProperty()\n                            .unbindBidirectional(Options.concatenateMemosProperty()));\n        }\n    }\n\n    void show(final SlipType slipType, final Runnable runnable) {\n        switch (slipType) {\n            case DECREASE:\n                tabPane.getSelectionModel().select(debitTab);\n                break;\n            case INCREASE:\n                tabPane.getSelectionModel().select(creditTab);\n                break;\n        }\n\n        show(runnable);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/SplitTransactionSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\n\nimport jgnash.engine.TransactionEntry;\n\n/**\n * Split Transaction Entry Controller for Credits and Debits.\n *\n * @author Craig Cavanaugh\n */\npublic class SplitTransactionSlipController extends AbstractTransactionEntrySlipController {\n\n    @FXML\n    private Button enterButton;\n\n    private SlipType slipType;\n\n    void setSlipType(final SlipType slipType) {\n        this.slipType = slipType;\n    }\n\n    @FXML\n    protected void initialize() {\n        super.initialize();\n\n        enterButton.disableProperty().bind(validFormProperty().not());\n    }\n\n    @Override\n    TransactionEntry buildTransactionEntry() {\n        TransactionEntry entry = new TransactionEntry();\n        entry.setMemo(memoField.getText());\n\n        int signum = amountField.getDecimal().signum();\n\n        if ((slipType == SlipType.DECREASE && signum >= 0) || (slipType == SlipType.INCREASE && signum < 0)) {\n            entry.setCreditAccount(accountExchangePane.getSelectedAccount());\n            entry.setDebitAccount(account.get());\n\n            if (hasEqualCurrencies()) {\n                entry.setAmount(amountField.getDecimal().abs());\n            } else {\n                entry.setDebitAmount(amountField.getDecimal().abs().negate());\n                entry.setCreditAmount(accountExchangePane.exchangeAmountProperty().get().abs());\n            }\n        } else {\n            entry.setCreditAccount(account.get());\n            entry.setDebitAccount(accountExchangePane.getSelectedAccount());\n\n            if (hasEqualCurrencies()) {\n                entry.setAmount(amountField.getDecimal().abs());\n            } else {\n                entry.setCreditAmount(amountField.getDecimal().abs());\n                entry.setDebitAmount(accountExchangePane.exchangeAmountProperty().get().abs().negate());\n            }\n        }\n\n        entry.setReconciled(account.get(), getReconciledState());\n        entry.setTags(tagPane.getSelectedTags());\n\n        return entry;\n    }\n\n    void modifyTransactionEntry(final TransactionEntry entry) {\n        oldEntry = entry;\n\n        memoField.setText(entry.getMemo());\n\n        if (slipType == SlipType.DECREASE) {\n            accountExchangePane.setSelectedAccount(entry.getCreditAccount());\n            amountField.setDecimal(entry.getDebitAmount().abs());\n\n            accountExchangePane.setExchangedAmount(entry.getCreditAmount());\n        } else {\n            accountExchangePane.setSelectedAccount(entry.getDebitAccount());\n            amountField.setDecimal(entry.getCreditAmount());\n\n            accountExchangePane.setExchangedAmount(entry.getDebitAmount().abs());\n        }\n\n        setReconciledState(entry.getReconciled(account.get()));\n\n        tagPane.setSelectedTags(entry.getTags());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionCommodityFormatTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\n\n/**\n * Table cell for Transaction amounts.\n * <p>\n * Applies a style to transaction amounts\n *\n * @author Craig Cavanaugh\n */\nclass TransactionCommodityFormatTableCell extends AbstractTransactionTableCell {\n\n    private final NumberFormat format;\n\n    TransactionCommodityFormatTableCell(final NumberFormat format) {\n        super();\n\n        this.format = format;\n    }\n\n    @Override\n    protected void updateItem(final BigDecimal amount, final boolean empty) {\n        super.updateItem(amount, empty);  // required\n\n        if (!empty && amount != null) {\n            applyFormat(amount, format);\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionDateTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\n\nimport javafx.scene.control.TableCell;\n\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.time.DateUtils;\n\n/**\n * Table cell for Transaction dates.\n * <p>\n * Applies a style if the transaction occurs in the future.\n *\n * @author Craig Cavanaugh\n */\nclass TransactionDateTableCell extends TableCell<Transaction, LocalDate> {\n\n    private final DateTimeFormatter dateFormatter = DateUtils.getShortDateFormatter();\n\n    @Override\n    protected void updateItem(final LocalDate date, final boolean empty) {\n        super.updateItem(date, empty);  // required\n\n        if (!empty && date != null) {\n            setText(dateFormatter.format(date));\n\n            if (date.isAfter(LocalDate.now())) {\n                setId(StyleClass.ITALIC_CELL_ID);\n            } else {\n                setId(StyleClass.NORMAL_CELL_ID);\n            }\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionDateTimeTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\n\nimport javafx.scene.control.TableCell;\n\nimport jgnash.engine.Transaction;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.skin.StyleClass;\n\n/**\n * Table cell for Transaction dates.\n * <p>\n * Applies a style if the transaction occurs in the future.\n *\n * @author Craig Cavanaugh\n */\nclass TransactionDateTimeTableCell extends TableCell<Transaction, LocalDateTime> {\n\n    private final DateTimeFormatter dateFormatter = DateUtils.getShortDateTimeFormatter();\n\n    @Override\n    protected void updateItem(final LocalDateTime date, final boolean empty) {\n        super.updateItem(date, empty);  // required\n\n        if (!empty && date != null) {\n            setText(dateFormatter.format(date));\n\n            if (date.isAfter(LocalDateTime.now())) {\n                setId(StyleClass.ITALIC_CELL_ID);\n            } else {\n                setId(StyleClass.NORMAL_CELL_ID);\n            }\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionDialog.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.IOException;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.control.Tab;\nimport javafx.scene.control.TabPane;\nimport javafx.scene.layout.Pane;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.StageUtils;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * A Dialog for creating and editing new transactions.\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionDialog extends Stage {\n\n    @FXML\n    private TabPane tabPane;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private final ObjectProperty<Consumer<Transaction>> transactionConsumer = new SimpleObjectProperty<>();\n\n    private Tab creditTab;\n    private Tab debitTab;\n\n    private TransactionDialog() {\n        FXMLUtils.loadFXML(this, \"TransactionDialog.fxml\", ResourceUtils.getBundle());\n\n        setTitle(ResourceUtils.getString(\"Title.NewTrans\"));\n    }\n\n    private ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    private void setTransactionConsumer(final Consumer<Transaction> consumer) {\n        transactionConsumer.set(consumer);\n    }\n\n    @FXML\n    private void initialize() {\n        accountProperty().addListener((observable, oldValue, newValue) -> buildTabs());\n    }\n\n    private Tab buildTab(final String tabName, final SlipType slipType) {\n\n        try {\n            final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"BankSlip.fxml\"), resources);\n            final Pane pane = fxmlLoader.load();\n\n            final SlipController slipController = fxmlLoader.getController();\n\n            // Override the default event handler for the enter and cancel buttons\n            slipController.enterButton.setOnAction(event -> handleEnterAction(slipController));\n            slipController.cancelButton.setOnAction(event -> tabPane.getScene().getWindow().hide());\n\n            slipController.setSlipType(slipType);\n            slipController.accountProperty().bind(accountProperty());\n\n            final Tab tab = new Tab(tabName);\n            tab.setContent(pane);\n            tab.setUserData(slipController);\n\n            return tab;\n        } catch (final IOException e) {\n            Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n        return new Tab();\n    }\n\n    private void buildTabs() {\n        final AccountType accountType = accountProperty().get().getAccountType();\n        final String[] tabNames = RegisterFactory.getCreditDebitTabNames(accountType);\n\n        creditTab = buildTab(tabNames[0], SlipType.INCREASE);\n        debitTab = buildTab(tabNames[1], SlipType.DECREASE);\n\n        tabPane.getTabs().addAll(creditTab, debitTab);\n\n        if (accountType == AccountType.CHECKING || accountType == AccountType.CREDIT) {\n            tabPane.getSelectionModel().select(debitTab);\n        } else if (accountType.getAccountGroup() == AccountGroup.INCOME) {\n            tabPane.getSelectionModel().select(debitTab);\n        }\n    }\n\n    private void handleEnterAction(final SlipController controller) {\n        transactionConsumer.get().accept(controller.buildTransaction());\n        tabPane.getScene().getWindow().hide();\n    }\n\n    private void setTransaction(final Transaction transaction) {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        if (!engine.isStored(transaction)) { // must not be a persisted transaction\n            if (transaction.getAmount(account.get()).signum() >= 0) {\n                tabPane.getSelectionModel().select(creditTab);\n                ((SlipController) creditTab.getUserData()).modifyTransaction(transaction);\n            } else {\n                tabPane.getSelectionModel().select(debitTab);\n                ((SlipController) debitTab.getUserData()).modifyTransaction(transaction);\n            }\n        }\n    }\n\n    public static void showAndWait(@NotNull final Account account, @Nullable final Transaction transaction,\n                                   final Consumer<Transaction> consumer) {\n\n        final TransactionDialog transactionDialog = new TransactionDialog();\n        transactionDialog.accountProperty().set(account);\n        transactionDialog.setTransactionConsumer(consumer);\n\n        JavaFXUtils.runLater(() -> {\n            transactionDialog.show();\n\n            // dialog must be shown with forms loaded prior to setting the transaction\n            if (transaction != null) {\n                transactionDialog.setTransaction(transaction);\n            }\n\n            // Size and lock the height of the dialog after it has been shown and transaction set\n            JavaFXUtils.runLater(() -> {\n                transactionDialog.sizeToScene();\n\n                transactionDialog.setMinHeight(transactionDialog.getHeight());\n                transactionDialog.setMaxHeight(transactionDialog.getHeight());\n            });\n\n            JavaFXUtils.runLater(() -> StageUtils.addBoundsListener(transactionDialog, TransactionDialog.class,\n                    MainView.getPrimaryStage()));\n        });\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionEntryCommodityFormatTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\n\nimport javafx.scene.control.TableCell;\n\nimport jgnash.engine.TransactionEntry;\nimport jgnash.uifx.skin.StyleClass;\n\n/**\n * Table cell for Transaction amounts.\n * <p>\n * Applies a style if the amount is negative\n *\n * @author Craig Cavanaugh\n */\nclass TransactionEntryCommodityFormatTableCell extends TableCell<TransactionEntry, BigDecimal> {\n\n    private final NumberFormat format;\n\n    TransactionEntryCommodityFormatTableCell(final NumberFormat format) {\n        this.format = format;\n\n        // Right align\n        setStyle(\"-fx-alignment: center-right;\");\n    }\n\n    @Override\n    protected void updateItem(final BigDecimal amount, final boolean empty) {\n        super.updateItem(amount, empty);  // required\n\n        if (!empty && amount != null) {\n            setText(format.format(amount));\n\n            // Not empty and amount is not null, but tableRow can be null... JavaFx Bug?\n            if (getTableRow() != null && getTableRow().getItem() != null) {\n                final boolean negative = amount.signum() < 0;\n\n                // Set font style\n                if (negative) {\n                    setId(StyleClass.NORMAL_NEGATIVE_CELL_ID);\n                } else {\n                    setId(StyleClass.NORMAL_CELL_ID);\n                }\n            }\n\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionStringTableCell.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.time.LocalDate;\n\nimport javafx.scene.control.TableCell;\n\nimport jgnash.engine.Transaction;\nimport jgnash.uifx.skin.StyleClass;\n\n/**\n * Table cell for Transaction strings.\n * <p>\n * Applies a style if the transaction occurs in the future.\n *\n * @author Craig Cavanaugh\n */\nclass TransactionStringTableCell extends TableCell<Transaction, String> {\n\n    @Override\n    protected void updateItem(final String string, final boolean empty) {\n        super.updateItem(string, empty);  // required\n\n        if (!empty && string != null) {\n            setText(string);\n\n            if (getTableRow() != null) { // null can occur if UI is very busy... JavaFX bug?\n                if (getTableRow().getItem() != null) {\n                    final boolean future = getTableRow().getItem().getLocalDate()\n                            .isAfter(LocalDate.now());\n\n                    if (future) {\n                        setId(StyleClass.ITALIC_CELL_ID);\n                    } else {\n                        setId(StyleClass.NORMAL_CELL_ID);\n                    }\n                }\n            }\n        } else {\n            setText(null);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionTagDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.geometry.Pos;\nimport javafx.scene.Node;\nimport javafx.scene.Scene;\nimport javafx.scene.control.CheckBox;\nimport javafx.scene.layout.TilePane;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Tag;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Allow tag selection for a transaction\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionTagDialogController {\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private TilePane tilePane;\n\n    private final AtomicBoolean tagsLoaded = new AtomicBoolean(false);\n\n    private final Set<Tag> originalTagSelection = new HashSet<>();\n\n    private boolean okay = false;\n\n    @FXML\n    private void initialize() {\n        tilePane.setId(\"tag-box\");\n        JavaFXUtils.runLater(this::loadTags);\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage)parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleClearAllAction() {\n        for (final Node node : tilePane.getChildren()) {\n            ((CheckBox)node).setSelected(false);\n        }\n    }\n\n    @FXML\n    private void handleOkAction() {\n        okay = true;\n        handleCloseAction();\n    }\n\n    private void loadTags() {\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final List<Tag> tagList = new ArrayList<>(engine.getTags());\n        Collections.sort(tagList);\n\n        for (final Tag tag : tagList) {\n            final CheckBox checkBox = new CheckBox(tag.getName());\n\n            checkBox.setGraphic(MaterialDesignLabel.fromInteger(tag.getShape(),\n                    MaterialDesignLabel.DEFAULT_SIZE * TransactionTagPane.ICON_SCALE, tag.getColor()));\n\n            checkBox.setUserData(tag);\n            TilePane.setAlignment(checkBox, Pos.BASELINE_LEFT);\n            tilePane.getChildren().add(checkBox);\n        }\n\n        tagsLoaded.set(true);\n    }\n\n    void setSelectedTags(final Collection<Tag> tags) {\n\n        originalTagSelection.addAll(tags);  // create a copy of the old selection\n\n        JavaFXUtils.runLater(() -> {\n            while (!tagsLoaded.get()) {\n                Thread.onSpinWait();\n            }\n\n            for (final Node node : tilePane.getChildren()) {\n                for (final Tag tag : tags) {\n                    if (tag.equals(node.getUserData())) {\n                        JavaFXUtils.runLater(() -> ((CheckBox)node).setSelected(true));\n                    }\n                }\n            }\n        });\n    }\n\n    Set<Tag> getSelectedTags() {\n        if (!okay) {\n            return originalTagSelection;\n        }\n\n        final Set<Tag> tagSet = new HashSet<>();\n\n        for (final Node node : tilePane.getChildren()) {\n            if ( ((CheckBox)node).isSelected()) {\n              tagSet.add((Tag)node.getUserData());\n            }\n        }\n\n        return tagSet;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransactionTagPane.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport java.io.IOException;\nimport java.util.Collection;\n\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableSet;\nimport javafx.fxml.FXML;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.HBox;\n\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Tag;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Controller for managing the Transaction tags pane\n *\n * @author Craig Cavanaugh\n */\npublic class TransactionTagPane extends GridPane implements MessageListener {\n\n    public static final double ICON_SCALE = 1.2;\n\n    private final ObservableSet<Tag> selectedTags = FXCollections.observableSet();\n\n    @FXML\n    private Button selectTagsButton;\n\n    @FXML\n    private HBox tagBox;\n\n    public TransactionTagPane() {\n        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"TransactionTagPane.fxml\"),\n                ResourceUtils.getBundle());\n\n        fxmlLoader.setRoot(this);\n        fxmlLoader.setController(this);\n\n        try {\n            fxmlLoader.load();\n        } catch (final IOException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @FXML\n    private void initialize() {\n        tagBox.setId(\"tag-pane\");\n\n        selectTagsButton.setOnAction(event -> showPopup());\n\n        updateVisibility();\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM, MessageChannel.TAG);\n    }\n\n    void setReadOnly(boolean readOnly) {\n        selectTagsButton.setDisable(readOnly);\n    }\n\n    void clearSelectedTags() {\n        selectedTags.clear();\n        JavaFXUtils.runLater(this::refreshTagView);\n    }\n\n    void setSelectedTags(final Collection<Tag> tags) {\n        selectedTags.clear();\n        selectedTags.addAll(tags);\n\n        JavaFXUtils.runLater(this::refreshTagView);\n    }\n\n    ObservableSet<Tag> getSelectedTags() {\n        return selectedTags;\n    }\n\n    private void showPopup() {\n        final FXMLUtils.Pair<TransactionTagDialogController> pair =\n                FXMLUtils.load(TransactionTagDialogController.class.getResource(\"TransactionTagDialog.fxml\"),\n                        ResourceUtils.getString(\"Title.SelTransTags\"));\n\n        final TransactionTagDialogController controller = pair.getController();\n\n        controller.setSelectedTags(selectedTags);\n\n        pair.getStage().showAndWait();\n\n        selectedTags.clear();\n        selectedTags.addAll(controller.getSelectedTags());\n\n        JavaFXUtils.runLater(this::refreshTagView);\n    }\n\n    void refreshTagView() {\n        tagBox.getChildren().clear();\n\n        // the the icons / labels\n        for (final Tag tag : selectedTags) {\n            final Label label  = new Label(\"\", MaterialDesignLabel.fromInteger(tag.getShape(),\n                    MaterialDesignLabel.DEFAULT_SIZE * ICON_SCALE, tag.getColor()));\n            label.setTooltip(new Tooltip(tag.getName()));\n            tagBox.getChildren().add(label);\n        }\n    }\n\n    private void updateVisibility() {\n        new Thread(() -> {\n            Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n            if (engine != null) {\n                JavaFXUtils.runLater(() -> selectTagsButton.setVisible(!engine.getTags().isEmpty()));\n            }\n        }).start();\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        switch (message.getEvent()) {\n            case TAG_ADD:\n            case TAG_REMOVE:\n                updateVisibility();\n                break;\n            case TAG_MODIFY:\n                JavaFXUtils.runLater(this::refreshTagView);\n                break;\n            case FILE_CLOSING:\n                MessageBus.getInstance().unregisterListener(this, MessageChannel.SYSTEM, MessageChannel.TAG);\n            default:\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/TransferSlipController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register;\n\nimport javafx.beans.binding.Bindings;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\n\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.util.NotNull;\n\n/**\n * Transaction Entry Controller for Transfers.\n *\n * @author Craig Cavanaugh\n */\npublic class TransferSlipController extends AbstractSlipController {\n\n    @FXML\n    private Button enterButton;\n\n    @FXML\n    private AccountExchangePane accountExchangePane;\n\n    @FXML\n    @Override\n    public void initialize() {\n        super.initialize();\n\n        validFormProperty.bind(amountField.validDecimalProperty()\n                .and(Bindings.isNotNull(accountExchangePane.selectedAccountProperty())));\n\n        // Bind necessary properties to the exchange panel\n        accountExchangePane.baseAccountProperty().bind(accountProperty());\n        accountExchangePane.amountProperty().bindBidirectional(amountField.decimalProperty());\n        accountExchangePane.amountEditableProperty().bind(amountField.editableProperty());\n\n        numberComboBox.setValue(resources.getString(\"Item.Trans\"));\n\n        enterButton.disableProperty().bind(validFormProperty.not());\n    }\n\n    @NotNull\n    @Override\n    public Transaction buildTransaction() {\n        String number = numberComboBox.getValue();\n\n        String payee = resources.getString(\"Tab.Transfer\");\n\n        Transaction transaction;\n\n        final int signum = amountField.getDecimal().signum();\n\n        if (signum >= 0) {\n            if (hasEqualCurrencies()) {\n                transaction =\n                        TransactionFactory.generateDoubleEntryTransaction(accountExchangePane.getSelectedAccount(),\n                        accountProperty().get(), amountField.getDecimal().abs(), datePicker.getValue(),\n                                memoTextField.getText(), payee, number);\n            } else {\n                transaction =\n                        TransactionFactory.generateDoubleEntryTransaction(accountExchangePane.getSelectedAccount(),\n                        accountProperty().get(), accountExchangePane.exchangeAmountProperty().get().abs(),\n                        amountField.getDecimal().abs().negate(), datePicker.getValue(), memoTextField.getText(), payee,\n                        number);\n            }\n        } else {\n            if (hasEqualCurrencies()) {\n                transaction = TransactionFactory.generateDoubleEntryTransaction(accountProperty().get(),\n                        accountExchangePane.getSelectedAccount(), amountField.getDecimal().abs(), datePicker.getValue(),\n                        memoTextField.getText(), payee, number);\n            } else {\n                transaction = TransactionFactory.generateDoubleEntryTransaction(accountProperty().get(),\n                        accountExchangePane.getSelectedAccount(), amountField.getDecimal().abs(),\n                        accountExchangePane.exchangeAmountProperty().get().abs().negate(), datePicker.getValue(),\n                        memoTextField.getText(), payee, number);\n            }\n        }\n\n        transaction.setTags(tagPane.getSelectedTags());\n\n        ReconcileManager.reconcileTransaction(accountProperty().get(), transaction, getReconciledState());\n\n        return transaction;\n    }\n\n    private boolean hasEqualCurrencies() {\n        return account.get().getCurrencyNode().equals(accountExchangePane.getSelectedAccount().getCurrencyNode());\n    }\n\n    @Override\n    public void modifyTransaction(@NotNull final Transaction transaction) {\n        throw new RuntimeException(\"Use for modification is not supported\");\n    }\n\n    @Override\n    boolean canModifyTransaction(final Transaction t) {\n        return false;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/reconcile/ReconcileDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register.reconcile;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.stream.Collectors;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.beans.value.ChangeListener;\nimport javafx.beans.value.ObservableValue;\nimport javafx.beans.value.WeakChangeListener;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.FilteredList;\nimport javafx.concurrent.Task;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.TitledPane;\nimport javafx.stage.Stage;\nimport javafx.util.Callback;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.MathConstants;\nimport jgnash.engine.RecTransaction;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.engine.ReconciledState;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.engine.message.MessageProperty;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.BigDecimalTableCell;\nimport jgnash.uifx.control.ShortDateTableCell;\nimport jgnash.uifx.skin.StyleClass;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.TableViewManager;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\nimport jgnash.uifx.views.register.RegisterFactory;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\n/**\n * Account reconcile dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class ReconcileDialogController implements MessageListener {\n\n    private static final String INCREASE_KEY = \"increase\";\n\n    private static final String DECREASE_KEY = \"decrease\";\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private Button finishButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private TitledPane decreaseTitledPane;\n\n    @FXML\n    private TitledPane increaseTitledPane;\n\n    @FXML\n    private Label increaseTotalLabel;\n\n    @FXML\n    private Label decreaseTotalLabel;\n\n    @FXML\n    private TableView<RecTransaction> increaseTableView;\n\n    @FXML\n    private TableView<RecTransaction> decreaseTableView;\n\n    @FXML\n    private Label openingBalanceLabel;\n\n    @FXML\n    private Label targetBalanceLabel;\n\n    @FXML\n    private Label reconciledBalanceLabel;\n\n    @FXML\n    private Label differenceLabel;\n\n    private static final double[] PREF_COLUMN_WEIGHTS = {0, 0, 0, 100, 0};\n\n    private Account account;\n\n    private LocalDate closingDate;\n\n    private BigDecimal openingBalance;\n\n    private BigDecimal endingBalance;\n\n    private final ObservableList<RecTransaction> transactions = FXCollections.observableArrayList();\n\n    private final FilteredList<RecTransaction> increaseList = new FilteredList<>(transactions);\n\n    private final FilteredList<RecTransaction> decreaseList = new FilteredList<>(transactions);\n\n    private NumberFormat numberFormat;\n\n    private final SimpleBooleanProperty reconciled = new SimpleBooleanProperty(false);\n\n    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ChangeListener<Number> widthListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ToggleStateChangeListener increaseTableStateChangeListener;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private ToggleStateChangeListener decreaseTableStateChangeListener;\n\n    private static final String PREF_NODE = \"/jgnash/uifx/views/register/reconcile\";\n\n    @FXML\n    private void initialize() {\n        // toggle the selection state\n        increaseTableStateChangeListener = new ToggleStateChangeListener(increaseTableView);\n        increaseTableView.getSelectionModel().selectedItemProperty()\n                .addListener(new WeakChangeListener<>(increaseTableStateChangeListener));\n\n        // toggle the selection state\n        decreaseTableStateChangeListener = new ToggleStateChangeListener(decreaseTableView);\n        decreaseTableView.getSelectionModel().selectedItemProperty()\n                .addListener(new WeakChangeListener<>(decreaseTableStateChangeListener));\n\n        finishButton.disableProperty().bind(reconciled.not());\n    }\n\n    private static Callback<Integer, Double> getColumnWeightFactory() {\n        return param -> PREF_COLUMN_WEIGHTS[param];\n    }\n\n    void initialize(final Account account, final LocalDate closingDate, final BigDecimal openingBalance,\n                    final BigDecimal endingBalance) {\n        Objects.requireNonNull(account);\n        Objects.requireNonNull(closingDate);\n        Objects.requireNonNull(openingBalance);\n        Objects.requireNonNull(endingBalance);\n\n        this.account = account;\n        this.closingDate = closingDate;\n        this.endingBalance = endingBalance;\n        this.openingBalance = openingBalance;\n\n        numberFormat = NumericFormats.getShortCommodityFormat(account.getCurrencyNode());\n\n        final String[] columnNames = RegisterFactory.getCreditDebitTabNames(account.getAccountType());\n\n        increaseTitledPane.setText(columnNames[0]);\n        decreaseTitledPane.setText(columnNames[1]);\n\n        increaseList.setPredicate(recTransaction -> recTransaction.getAmount(account).signum() >= 0);\n        decreaseList.setPredicate(recTransaction -> recTransaction.getAmount(account).signum() < 0);\n\n        openingBalanceLabel.setText(numberFormat.format(openingBalance));\n        targetBalanceLabel.setText(numberFormat.format(endingBalance));\n\n        loadTables();\n\n        MessageBus.getInstance().registerListener(this, MessageChannel.TRANSACTION);\n    }\n\n    private void loadTables() {\n        final TableViewManager<RecTransaction> increaseTableViewManager\n                = new TableViewManager<>(increaseTableView, PREF_NODE);\n        increaseTableViewManager.setColumnWeightFactory(getColumnWeightFactory());\n        increaseTableViewManager.setPreferenceKeyFactory(() -> INCREASE_KEY);\n\n        final TableViewManager<RecTransaction> decreaseTableViewManager\n                = new TableViewManager<>(decreaseTableView, PREF_NODE);\n        decreaseTableViewManager.setColumnWeightFactory(getColumnWeightFactory());\n        decreaseTableViewManager.setPreferenceKeyFactory(() -> DECREASE_KEY);\n\n        transactions.addAll(account.getSortedTransactionList().stream().filter(this::reconcilable)\n                .map(transaction -> new RecTransaction(transaction, transaction.getReconciled(account)))\n                .collect(Collectors.toList()));\n\n        configureTableView(increaseTableView, increaseTableViewManager);\n        configureTableView(decreaseTableView, decreaseTableViewManager);\n\n        increaseTableView.setItems(increaseList);\n        decreaseTableView.setItems(decreaseList);\n\n        updateCalculatedValues();\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        MessageBus.getInstance().unregisterListener(this, MessageChannel.TRANSACTION);\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleDecreaseSelectAllAction() {\n        setReconciledState(decreaseList, ReconciledState.RECONCILED, decreaseTableView);\n    }\n\n    @FXML\n    private void handleDecreaseClearAllAction() {\n        setReconciledState(decreaseList, ReconciledState.NOT_RECONCILED, decreaseTableView);\n    }\n\n    @FXML\n    private void handleIncreaseSelectAllAction() {\n        setReconciledState(increaseList, ReconciledState.RECONCILED, increaseTableView);\n    }\n\n    @FXML\n    private void handleIncreaseClearAllAction() {\n        setReconciledState(increaseList, ReconciledState.NOT_RECONCILED, increaseTableView);\n    }\n\n    @FXML\n    private void handleFinishLaterAction() {\n        final Task<Void> commitTask = new Task<>() {\n            @Override\n            protected Void call() {\n                updateMessage(resources.getString(\"Message.PleaseWait\"));\n                updateProgress(-1, Long.MAX_VALUE);\n\n                ReconcileManager.reconcileTransactions(account, transactions, ReconciledState.CLEARED);\n                return null;\n            }\n        };\n\n        new Thread(commitTask).start();\n\n        StaticUIMethods.displayTaskProgress(commitTask);\n\n        handleCloseAction();\n    }\n\n    @FXML\n    private void handleFinishAction() {\n\n        final Task<Void> commitTask = new Task<>() {\n            @Override\n            protected Void call() {\n                updateMessage(resources.getString(\"Message.PleaseWait\"));\n                updateProgress(-1, Long.MAX_VALUE);\n\n                ReconcileManager.reconcileTransactions(account, transactions, ReconciledState.RECONCILED);\n                ReconcileManager.setAccountDateAttribute(account, Account.RECONCILE_LAST_SUCCESS_DATE, closingDate);\n                return null;\n            }\n        };\n\n        new Thread(commitTask).start();\n\n        StaticUIMethods.displayTaskProgress(commitTask);\n\n        handleCloseAction();\n    }\n\n    private void setReconciledState(final List<RecTransaction> transactionList, final ReconciledState reconciledState,\n                                    final TableView<RecTransaction> tableView) {\n        readWriteLock.readLock().lock();\n        try {\n            for (final RecTransaction recTransaction : transactionList) {\n                recTransaction.setReconciledState(reconciledState);\n            }\n            tableView.refresh();\n            updateCalculatedValues();\n        } finally {\n            readWriteLock.readLock().unlock();\n        }\n    }\n\n    private void updateCalculatedValues() {\n        new Thread(() -> {\n\n            final BigDecimal increaseAmount = getReconciledTotal(increaseList);\n            final BigDecimal decreaseAmount = getReconciledTotal(decreaseList);\n\n            JavaFXUtils.runLater(() -> {\n                increaseTotalLabel.setText(numberFormat.format(increaseAmount));\n                decreaseTotalLabel.setText(numberFormat.format(decreaseAmount));\n            });\n\n            final BigDecimal reconciledBalance = increaseAmount.add(decreaseAmount).add(openingBalance);\n\n            JavaFXUtils.runLater(() -> reconciledBalanceLabel\n                    .setText(numberFormat.format(reconciledBalance)));\n\n            // need to round of the values for difference to work (investment accounts)\n            final int scale = account.getCurrencyNode().getScale();\n\n            final BigDecimal difference = endingBalance.subtract(reconciledBalance).abs()\n                    .setScale(scale, MathConstants.roundingMode);\n\n            JavaFXUtils.runLater(() -> differenceLabel.setText(numberFormat.format(difference)));\n\n            reconciled.set(difference.compareTo(BigDecimal.ZERO) == 0);\n        }).start();\n    }\n\n    private BigDecimal getReconciledTotal(final List<RecTransaction> list) {\n        BigDecimal sum = BigDecimal.ZERO;\n\n        readWriteLock.readLock().lock();\n\n        try {\n            for (final RecTransaction t : list) {\n                if (t.getReconciledState() != ReconciledState.NOT_RECONCILED) {\n                    sum = sum.add(t.getAmount(account));\n                }\n            }\n        } finally {\n            readWriteLock.readLock().unlock();\n        }\n\n        return AccountBalanceDisplayManager.convertToSelectedBalanceMode(account.getAccountType(), sum);\n    }\n\n    private void configureTableView(final TableView<RecTransaction> tableView, final TableViewManager<RecTransaction> tableViewManager) {\n        tableView.setTableMenuButtonVisible(false);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n\n        final TableColumn<RecTransaction, String> reconciledColumn = new TableColumn<>(resources.getString(\"Column.Clr\"));\n        reconciledColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getReconciledState().toString()));\n        tableView.getColumns().add(reconciledColumn);\n\n        final TableColumn<RecTransaction, LocalDate> dateColumn = new TableColumn<>(resources.getString(\"Column.Date\"));\n        dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getDate()));\n        dateColumn.setCellFactory(param -> new ShortDateTableCell<>());\n        tableView.getColumns().add(dateColumn);\n\n        final TableColumn<RecTransaction, String> numberColumn = new TableColumn<>(resources.getString(\"Column.Num\"));\n        numberColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getNumber()));\n        tableView.getColumns().add(numberColumn);\n\n        final TableColumn<RecTransaction, String> payeeColumn = new TableColumn<>(resources.getString(\"Column.Payee\"));\n        payeeColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getPayee()));\n        tableView.getColumns().add(payeeColumn);\n\n        final TableColumn<RecTransaction, BigDecimal> amountColumn =\n                new TableColumn<>(resources.getString(\"Column.Amount\"));\n        amountColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(AccountBalanceDisplayManager.\n                convertToSelectedBalanceMode(account.getAccountType(), param.getValue().getAmount(account))));\n        amountColumn.setCellFactory(param -> new BigDecimalTableCell<>(\n                NumericFormats.getShortCommodityFormat(account.getCurrencyNode())));\n\n        tableView.getColumns().add(amountColumn);\n\n        // hide the horizontal scrollbar\n        tableView.getStylesheets().addAll(StyleClass.HIDE_HORIZONTAL_CSS);\n\n        tableViewManager.setColumnFormatFactory(param -> {\n            if (param == amountColumn && account != null) {\n                return NumericFormats.getShortCommodityFormat(account.getCurrencyNode());\n            }\n            return null;\n        });\n\n        widthListener = new NumberChangeListener(tableViewManager, tableView);\n\n        tableView.widthProperty().addListener(new WeakChangeListener<>(widthListener));\n    }\n\n    private boolean reconcilable(final Transaction t) {\n        return DateUtils.before(t.getLocalDate(), closingDate) && t.getReconciled(account) != ReconciledState.RECONCILED;\n    }\n\n    @Nullable\n    private synchronized RecTransaction findTransaction(@NotNull final Transaction t) {\n        readWriteLock.readLock().lock();\n\n        try {\n            for (final RecTransaction tran : transactions) {\n                if (tran.getTransaction() == t) {\n                    return tran;\n                }\n            }\n            return null;\n        } finally {\n            readWriteLock.readLock().unlock();\n        }\n    }\n\n    @Override\n    public void messagePosted(final Message message) {\n        if (account != null && account.equals(message.getObject(MessageProperty.ACCOUNT))) {\n            final Transaction transaction = message.getObject(MessageProperty.TRANSACTION);\n\n            if (transaction != null) {\n                switch (message.getEvent()) {\n                    case TRANSACTION_REMOVE:\n                        final RecTransaction trans = findTransaction(transaction);\n\n                        if (trans != null) {\n                            readWriteLock.writeLock().lock();\n                            try {\n                                transactions.removeAll(trans);\n                                updateCalculatedValues();\n                            } finally {\n                                readWriteLock.writeLock().unlock();\n                            }\n                        }\n                        break;\n                    case TRANSACTION_ADD:\n                        if (reconcilable(transaction)) {\n                            readWriteLock.writeLock().lock();\n                            try {\n                                transactions.add(new RecTransaction(transaction, transaction.getReconciled(account)));\n                                FXCollections.sort(transactions);\n                                updateCalculatedValues();\n                            } finally {\n                                readWriteLock.writeLock().unlock();\n                            }\n                        }\n                        break;\n                    default:\n                        break;\n                }\n            }\n        }\n    }\n\n    private static class NumberChangeListener implements ChangeListener<Number> {\n        private final TableViewManager<RecTransaction> tableViewManager;\n        private final TableView<RecTransaction> tableView;\n\n        NumberChangeListener(final TableViewManager<RecTransaction> tableViewManager, final TableView<RecTransaction> tableView) {\n            this.tableViewManager = tableViewManager;\n            this.tableView = tableView;\n        }\n\n        @Override\n        public void changed(final ObservableValue<? extends Number> observable, final Number oldValue, final Number newValue) {\n\n            if (newValue != null && newValue.doubleValue() > 0) {\n                JavaFXUtils.runLater(tableViewManager::restoreLayout);\n                JavaFXUtils.runLater(tableViewManager::packTable);\n\n                tableView.widthProperty().removeListener(this);\n            }\n        }\n    }\n\n    // Selection listener that toggles the reconciled state\n    private class ToggleStateChangeListener implements ChangeListener<RecTransaction> {\n\n        private final TableView<RecTransaction> tableView;\n\n        private ToggleStateChangeListener(final TableView<RecTransaction> tableView) {\n            this.tableView = tableView;\n        }\n\n        @Override\n        public void changed(final ObservableValue<? extends RecTransaction> observable,\n                            final RecTransaction oldValue, final RecTransaction newValue) {\n            if (newValue != null) {\n                if (newValue.getReconciledState() == ReconciledState.RECONCILED) {\n                    newValue.setReconciledState(ReconciledState.NOT_RECONCILED);\n                } else {\n                    newValue.setReconciledState(ReconciledState.RECONCILED);\n                }\n                tableView.refresh();\n                JavaFXUtils.runLater(() -> tableView.getSelectionModel().clearSelection());\n                updateCalculatedValues();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/views/register/reconcile/ReconcileSettingsDialogController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.views.register.reconcile;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalAdjusters;\nimport java.util.Objects;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.Scene;\nimport javafx.scene.control.ButtonBar;\nimport javafx.scene.control.CheckBox;\nimport javafx.stage.Modality;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.ReconcileManager;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.InjectFXML;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.AccountBalanceDisplayManager;\n\n/**\n * Account reconcile settings dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class ReconcileSettingsDialogController {\n\n    private static final String CALC_BAL = \"calcClosingBalance\";\n\n    private static final int FUZZY_DATE_RANGE = 2;\n\n    @InjectFXML\n    private final ObjectProperty<Scene> parent = new SimpleObjectProperty<>();\n\n    @FXML\n    private ButtonBar buttonBar;\n\n    @FXML\n    private DecimalTextField openingBalanceTextField;\n\n    @FXML\n    private DecimalTextField closingBalanceTextField;\n\n    @FXML\n    private CheckBox autoFillBalanceCheckBox;\n\n    @FXML\n    private DatePickerEx datePicker;\n\n    private final ObjectProperty<Account> account = new SimpleObjectProperty<>();\n\n    private final Preferences preferences = Preferences.userNodeForPackage(ReconcileSettingsDialogController.class);\n\n    @FXML\n    private void initialize() {\n        buttonBar.buttonOrderProperty().bind(Options.buttonOrderProperty());\n\n        autoFillBalanceCheckBox.selectedProperty().set(preferences.getBoolean(CALC_BAL, false));\n\n        accountProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                determineBalances();\n            }\n        });\n    }\n\n    public ObjectProperty<Account> accountProperty() {\n        return account;\n    }\n\n    private void determineBalances() {\n        final Account account = accountProperty().get();\n\n        // Last date of the month for the 1st unreconciled transaction\n        LocalDate statementDate = account.getFirstUnreconciledTransactionDate().with(TemporalAdjusters.lastDayOfMonth());\n\n        // Balance at the 1st unreconciled transaction\n        BigDecimal openingBalance = AccountBalanceDisplayManager.convertToSelectedBalanceMode(account.getAccountType(),\n                account.getOpeningBalanceForReconcile());\n\n        // Balance at the statement date\n        BigDecimal closingBalance = AccountBalanceDisplayManager.convertToSelectedBalanceMode(account.getAccountType(),\n                account.getBalance(statementDate));\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n        Objects.requireNonNull(engine);\n\n        final LocalDate lastSuccessDate = ReconcileManager.getAccountDateAttribute(account,\n                Account.RECONCILE_LAST_SUCCESS_DATE).orElse(null);\n\n        final LocalDate lastAttemptDate = ReconcileManager.getAccountDateAttribute(account,\n                Account.RECONCILE_LAST_ATTEMPT_DATE).orElse(null);\n\n        final LocalDate lastStatementDate = ReconcileManager.getAccountDateAttribute(account,\n                Account.RECONCILE_LAST_STATEMENT_DATE).orElse(LocalDate.now());\n\n        final BigDecimal lastClosingBalance = ReconcileManager.getAccountBigDecimalAttribute(account,\n                Account.RECONCILE_LAST_CLOSING_BALANCE).orElse(null);\n\n        final BigDecimal lastOpeningBalance = ReconcileManager.getAccountBigDecimalAttribute(account,\n                Account.RECONCILE_LAST_OPENING_BALANCE).orElse(null);\n\n        if (lastSuccessDate != null) { // we had prior success, use a new date one month out if the date is earlier than today\n            if (DateUtils.before(lastStatementDate, LocalDate.now())) {\n\n                // set the new statement date\n                statementDate = lastStatementDate.plusMonths(1);\n\n                // use the account balance of the estimated statement date\n                closingBalance = AccountBalanceDisplayManager.convertToSelectedBalanceMode(account.getAccountType(), account.getBalance(statementDate));\n            }\n        }\n\n        // an recent attempt has been made before, override defaults\n        if (lastAttemptDate != null && Math.abs(ChronoUnit.DAYS.between(lastAttemptDate, LocalDate.now())) <= FUZZY_DATE_RANGE) {\n            statementDate = lastStatementDate; // set the new statement date + 1 month\n\n            if (lastOpeningBalance != null) {\n                openingBalance = lastOpeningBalance;\n            }\n\n            if (lastClosingBalance != null) {\n                closingBalance = lastClosingBalance;\n            }\n        }\n\n        datePicker.setValue(statementDate);\n        openingBalanceTextField.setDecimal(openingBalance);\n        closingBalanceTextField.setDecimal(closingBalance);\n    }\n\n    @FXML\n    private void handleUpdateBalance() {\n        if (autoFillBalanceCheckBox.isSelected()) {\n            final Account account = accountProperty().get();\n\n            // Balance at the statement date\n            final BigDecimal closingBalance = AccountBalanceDisplayManager\n                    .convertToSelectedBalanceMode(account.getAccountType(), account.getBalance(datePicker.getValue()));\n            closingBalanceTextField.setDecimal(closingBalance);\n\n            // Balance at the 1st unreconciled transaction\n            final BigDecimal openingBalance = AccountBalanceDisplayManager.convertToSelectedBalanceMode(account.getAccountType(),\n                    account.getOpeningBalanceForReconcile());\n            openingBalanceTextField.setDecimal(openingBalance);\n        }\n\n        preferences.putBoolean(CALC_BAL, autoFillBalanceCheckBox.isSelected());\n    }\n\n    @FXML\n    private void handleCloseAction() {\n        ((Stage) parent.get().getWindow()).close();\n    }\n\n    @FXML\n    private void handleOkayAction() {\n        final LocalDate statementDate = datePicker.getValue();\n        final BigDecimal openingBalance = openingBalanceTextField.getDecimal();\n        final BigDecimal closingBalance = closingBalanceTextField.getDecimal();\n\n        final FXMLUtils.Pair<ReconcileDialogController> pair =\n                FXMLUtils.load(ReconcileDialogController.class.getResource(\"ReconcileDialog.fxml\"),\n                        ResourceUtils.getString(\"Button.Reconcile\") + \" - \" + account.get().getPathName());\n\n        pair.getController().initialize(account.get(), statementDate, openingBalance, closingBalance);\n\n        // Override the defaults set by FXMLUtils\n        pair.getStage().initModality(Modality.NONE);\n        pair.getStage().initOwner(null);\n\n        JavaFXUtils.runLater(() -> {\n            pair.getStage().show();\n            pair.getStage().setMinWidth(pair.getStage().getWidth());\n            pair.getStage().setMinHeight(pair.getStage().getHeight());\n        });\n\n        // push account updates outside the UI thread to improve performance\n        new Thread(() -> {\n            ReconcileManager.setAccountDateAttribute(accountProperty().get(),\n                    Account.RECONCILE_LAST_ATTEMPT_DATE, LocalDate.now());\n\n            ReconcileManager.setAccountDateAttribute(accountProperty().get(),\n                    Account.RECONCILE_LAST_STATEMENT_DATE, statementDate);\n\n            ReconcileManager.setAccountBigDecimalAttribute(accountProperty().get(),\n                    Account.RECONCILE_LAST_OPENING_BALANCE, openingBalance);\n\n            ReconcileManager.setAccountBigDecimalAttribute(accountProperty().get(),\n                    Account.RECONCILE_LAST_CLOSING_BALANCE, closingBalance);\n        }).start();\n\n        handleCloseAction(); // close the dialog\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/file/NewFileFourController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.file;\n\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.ResourceBundle;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TreeItem;\nimport javafx.scene.control.TreeView;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountTreeXMLFactory;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.RootAccount;\nimport jgnash.uifx.control.CheckListView;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.TextResource;\n\n/**\n * New file wizard pane, handles selection of account sets.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileFourController extends AbstractWizardPaneController<NewFileWizard.Settings> {\n\n    @FXML\n    private TreeView<Account> accountTreeView;\n\n    @FXML\n    private CheckListView<RootAccount> accountSetsList;\n\n    @FXML\n    private TextArea textArea;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        textArea.textProperty().set(TextResource.getString(\"NewFileFour.txt\"));\n        accountSetsList.getItems().addAll(AccountTreeXMLFactory.getLocalizedAccountSet());\n\n        accountSetsList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue)\n                -> showAccountSet(newValue));\n\n        updateDescriptor();\n    }\n\n    private void showAccountSet(final RootAccount rootAccount) {\n        final TreeItem<Account> root = new TreeItem<>(rootAccount);\n        root.setExpanded(true);\n\n        accountTreeView.setRoot(root);\n        loadChildren(root);\n    }\n\n    private void loadChildren(final TreeItem<Account> parentItem) {\n        parentItem.getValue().getChildren(Comparators.getAccountByCode()).forEach(child ->\n        {\n            final TreeItem<Account> childItem = new TreeItem<>(child);\n            childItem.setExpanded(true);\n            parentItem.getChildren().add(childItem);\n\n            if (child.getChildCount() > 0) {\n                loadChildren(childItem);\n            }\n        });\n    }\n\n    @Override   \n    public void putSettings(final Map<NewFileWizard.Settings, Object> map) {\n        map.put(NewFileWizard.Settings.ACCOUNT_SET,  new ArrayList<Account>(accountSetsList.getCheckedItems()));\n    }\n\n    @Override\n    public String toString() {\n        return \"4. \" + ResourceUtils.getString(\"Title.ChooseAccounts\");\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/file/NewFileOneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.file;\n\nimport java.io.File;\nimport java.util.Map;\nimport java.util.ResourceBundle;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.TextArea;\nimport javafx.scene.control.TextField;\nimport javafx.scene.paint.Color;\n\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.TextResource;\nimport jgnash.uifx.actions.DatabasePathAction;\nimport jgnash.uifx.control.DataStoreTypeComboBox;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.util.FileUtils;\n\n/**\n * New file wizard panel.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileOneController extends AbstractWizardPaneController<NewFileWizard.Settings> {\n\n    @FXML\n    private MaterialDesignLabel warningLabel;\n\n    @FXML\n    private TextArea textArea;\n\n    @FXML\n    private DataStoreTypeComboBox storageTypeComboBox;\n\n    @FXML\n    private Button fileButton;\n\n    @FXML\n    private TextField fileNameField;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        textArea.setText(TextResource.getString(\"NewFileOne.txt\"));\n        storageTypeComboBox.setValue(DataStoreType.H2_DATABASE);\n\n        storageTypeComboBox.valueProperty().addListener((observable, oldValue, newValue) -> checkForOverWrite());\n\n        fileNameField.textProperty().addListener((observable, oldValue, newValue) -> {\n            if (newValue != null) {\n                checkForOverWrite();\n            }\n        });\n\n        warningLabel.setColor(Color.GOLDENROD);\n\n        updateDescriptor();\n    }\n\n    @Override\n    public void putSettings(final Map<NewFileWizard.Settings, Object> map) {\n        map.put(NewFileWizard.Settings.DATABASE_NAME, fileNameField.getText());\n        map.put(NewFileWizard.Settings.TYPE, storageTypeComboBox.getValue());\n        map.put(NewFileWizard.Settings.PASSWORD, \"\");\n    }\n\n    @Override\n    public void getSettings(final Map<NewFileWizard.Settings, Object> map) {\n        DataStoreType type = (DataStoreType) map.get(NewFileWizard.Settings.TYPE);\n\n        if (type != null) {\n            storageTypeComboBox.setValue(type);\n        }\n\n        final String fileName = (String) map.get(NewFileWizard.Settings.DATABASE_NAME);\n\n        if (FileUtils.fileHasExtension(fileName)) {\n            fileNameField.setText(fileName);\n        } else {\n            fileNameField.setText(fileName + storageTypeComboBox.getValue().getDataStore().getFileExt());\n        }\n\n        updateDescriptor();\n    }\n\n    @Override\n    public boolean isPaneValid() {\n        return !fileNameField.getText().isEmpty();\n    }\n\n    @Override\n    public String toString() {\n        return \"1. \" + ResourceUtils.getString(\"Title.DatabaseCfg\");\n    }\n\n    @FXML\n    private void handleFileButtonAction() {\n        final File file = DatabasePathAction.getFileToSave();\n\n        if (file != null) {\n            fileNameField.setText(file.getAbsolutePath());\n        }\n\n        updateDescriptor();\n    }\n\n    @FXML\n    private void handleDataStoreTypeAction() {\n        if (!fileNameField.getText().isEmpty()) {\n\n            JavaFXUtils.runLater(() -> {\n                String fileName = FileUtils.stripFileExtension(fileNameField.getText());\n                fileNameField.setText(fileName + storageTypeComboBox.getValue().getDataStore().getFileExt());\n            });\n        }\n\n    }\n\n    private void checkForOverWrite() {\n        warningLabel.visibleProperty().set(EngineFactory.doesDatabaseExist(fileNameField.getText(),\n                storageTypeComboBox.getValue()));\n\n        warningLabel.managedProperty().bind(warningLabel.visibleProperty());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/file/NewFileSummaryController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.file;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.ResourceBundle;\n\nimport javafx.collections.FXCollections;\nimport javafx.collections.ObservableList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.TextField;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * New file wizard pane, shows summary.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileSummaryController extends AbstractWizardPaneController<NewFileWizard.Settings> {\n\n    @FXML\n    private TextField fileNameField;\n\n    @FXML\n    private TextField defaultCurrencyField;\n\n    @FXML\n    private ListView<CurrencyNode> currenciesList;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        updateDescriptor();\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void getSettings(final Map<NewFileWizard.Settings, Object> map) {\n        fileNameField.setText((String) map.get(NewFileWizard.Settings.DATABASE_NAME));\n        defaultCurrencyField.setText(map.get(NewFileWizard.Settings.DEFAULT_CURRENCY).toString());\n\n        final ObservableList<CurrencyNode> currencyNodes = FXCollections.observableArrayList();\n        currencyNodes.addAll((Collection<? extends CurrencyNode>) map.get(NewFileWizard.Settings.CURRENCIES));\n        currencyNodes.add((CurrencyNode) map.get(NewFileWizard.Settings.DEFAULT_CURRENCY));\n        FXCollections.sort(currencyNodes);\n\n        currenciesList.getItems().setAll(currencyNodes);\n    }\n\n    @Override\n    public String toString() {\n        return \"5. \" + ResourceUtils.getString(\"Title.Summary\");\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/file/NewFileThreeController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.file;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport javafx.collections.FXCollections;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ListView;\nimport javafx.scene.control.SelectionMode;\nimport javafx.scene.control.TextArea;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.TextResource;\n\n/**\n * New file wizard panel.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileThreeController extends AbstractWizardPaneController<NewFileWizard.Settings> {\n\n    @FXML\n    private ListView<CurrencyNode> selectedList;\n\n    @FXML\n    private ListView<CurrencyNode> availableList;\n\n    @FXML\n    private TextArea textArea;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        textArea.textProperty().set(TextResource.getString(\"NewFileThree.txt\"));\n\n        availableList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n        selectedList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);\n\n        updateDescriptor();\n    }\n\n    @Override   \n    public void putSettings(final Map<NewFileWizard.Settings, Object> map) {\n        map.put(NewFileWizard.Settings.CURRENCIES, new TreeSet<>(selectedList.getItems()));\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void getSettings(final Map<NewFileWizard.Settings, Object> map) {\n        if (availableList.getItems().isEmpty()) {\n            Set<CurrencyNode> currencies = (Set<CurrencyNode>) map.get(NewFileWizard.Settings.DEFAULT_CURRENCIES);\n\n            if (currencies != null) {\n                availableList.getItems().addAll(currencies);\n            }\n        }\n    }\n\n    @Override\n    public String toString() {\n        return \"3. \" + ResourceUtils.getString(\"Title.SelAvailCurr\");\n    }\n\n    @FXML\n    private void handleAddAction() {\n        swap(new ArrayList<>(availableList.getSelectionModel().getSelectedItems()), availableList, selectedList);\n    }\n\n    @FXML\n    private void handleRemoveAction() {\n        swap( new ArrayList<>(selectedList.getSelectionModel().getSelectedItems()), selectedList, availableList);\n    }\n\n    private void swap(final List<CurrencyNode> currencyNodes, final ListView<CurrencyNode> sourceListView,\n                      final ListView<CurrencyNode> destinationListView) {\n\n        sourceListView.getItems().removeAll(currencyNodes);\n        destinationListView.getItems().addAll(currencyNodes);\n        FXCollections.sort(destinationListView.getItems());\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/file/NewFileTwoController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.file;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport javafx.collections.ObservableList;\nimport javafx.collections.transformation.SortedList;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ComboBox;\nimport javafx.scene.control.TextArea;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DefaultCurrencies;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.TextResource;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\n\n/**\n * New file wizard panel.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileTwoController extends AbstractWizardPaneController<NewFileWizard.Settings> {\n\n    // Do not use a CurrencyComboBox at this point or it will boot the engine\n    @FXML\n    private ComboBox<CurrencyNode> defaultCurrencyComboBox;\n\n    @FXML\n    private TextArea textArea;\n\n    @FXML\n    private void initialize() {\n        textArea.setText(TextResource.getString(\"NewFileTwo.txt\"));\n\n        initDefaultCurrencies();\n    }\n\n    @Override\n    public void putSettings(final Map<NewFileWizard.Settings, Object> map) {\n        map.put(NewFileWizard.Settings.DEFAULT_CURRENCY, defaultCurrencyComboBox.getValue());\n    }\n\n    private void initDefaultCurrencies() {\n        final Set<CurrencyNode> currencyNodes = DefaultCurrencies.generateCurrencies();\n        final ObservableList<CurrencyNode> items = defaultCurrencyComboBox.getItems();\n\n        defaultCurrencyComboBox.setItems(new SortedList<>(items));\n        items.addAll(currencyNodes);\n\n        final String symbol = DefaultCurrencies.getDefault().getSymbol();\n\n        // set the default currency by matching the default locale\n        // if the default locale's currency is unknown, the currency symbol is \"XXX\" and this Optional will be empty\n        Optional<CurrencyNode> matchingCurrency = currencyNodes.stream()\n            .filter(node -> symbol.equals(node.getSymbol()))\n            .findAny();\n\n        defaultCurrencyComboBox.setValue(matchingCurrency.orElse(\n            currencyNodes.stream().findFirst().orElseThrow(() ->\n                new RuntimeException(\"Could not find any currencies\"))));\n\n        updateDescriptor();\n    }\n\n    @Override\n    public boolean isPaneValid() {\n        return defaultCurrencyComboBox.getValue() != null;\n    }\n\n    @Override\n    public String toString() {\n        return \"2. \" + ResourceUtils.getString(\"Title.DefDefCurr\");\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/file/NewFileWizard.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.file;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.Set;\n\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.layout.Pane;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.DefaultCurrencies;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.RootAccount;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.wizard.WizardDialogController;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NewFileUtility;\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Dialog for creating a new file.\n *\n * @author Craig Cavanaugh\n */\npublic class NewFileWizard {\n\n    public enum Settings {\n        CURRENCIES,\n        DEFAULT_CURRENCIES,\n        DEFAULT_CURRENCY,\n        DATABASE_NAME,\n        ACCOUNT_SET,\n        TYPE,\n        PASSWORD\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private NewFileWizard() {\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FXMLUtils.Pair<WizardDialogController<Settings>> pair =\n                FXMLUtils.load(WizardDialogController.class.getResource(\"WizardDialog.fxml\"),\n                        resources.getString(\"Title.NewFile\"));\n\n        final WizardDialogController<Settings> wizardController = pair.getController();\n\n        wizardController.setSetting(Settings.DATABASE_NAME, EngineFactory.getDefaultDatabase());\n        wizardController.setSetting(Settings.DEFAULT_CURRENCIES, DefaultCurrencies.generateCurrencies());\n\n        try {\n            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"NewFileOne.fxml\"), resources);\n            Pane pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n            fxmlLoader = new FXMLLoader(getClass().getResource(\"NewFileTwo.fxml\"), resources);\n            pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n            fxmlLoader = new FXMLLoader(getClass().getResource(\"NewFileThree.fxml\"), resources);\n            pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n            fxmlLoader = new FXMLLoader(getClass().getResource(\"NewFileFour.fxml\"), resources);\n            pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n            fxmlLoader = new FXMLLoader(getClass().getResource(\"NewFileSummary.fxml\"), resources);\n            pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n        } catch (final IOException e) {\n            StaticUIMethods.displayException(e);\n        }\n\n        pair.getStage().setResizable(false);\n        pair.getStage().showAndWait();\n\n        if (wizardController.validProperty().get()) {\n            final String database = (String) wizardController.getSetting(Settings.DATABASE_NAME);\n            final Set<CurrencyNode> nodes = (Set<CurrencyNode>) wizardController.getSetting(Settings.CURRENCIES);\n            final CurrencyNode defaultCurrency = (CurrencyNode) wizardController.getSetting(Settings.DEFAULT_CURRENCY);\n            final DataStoreType type = (DataStoreType) wizardController.getSetting(Settings.TYPE);\n            final String password = (String) wizardController.getSetting(Settings.PASSWORD);\n            final List<RootAccount> accountList = (List<RootAccount>) wizardController.getSetting(Settings.ACCOUNT_SET);\n\n            // Ensure file extension matches data store type\n            final String fileName = FileUtils.stripFileExtension(database) + type.getDataStore().getFileExt();\n\n            try {\n                NewFileUtility.buildNewFile(fileName, type, password.toCharArray(), defaultCurrency, nodes, accountList);\n            } catch (final IOException e) {\n                StaticUIMethods.displayError(e.getLocalizedMessage());\n            }\n        }\n    }\n\n    @SuppressWarnings({\"unused\", \"InstantiationOfUtilityClass\"})\n\tpublic static void showAndWait() {\n        new NewFileWizard();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/imports/ImportPageOneController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.imports;\n\nimport java.util.Map;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.ChoiceBox;\nimport javafx.scene.control.Label;\nimport javafx.scene.text.Text;\nimport javafx.scene.text.TextFlow;\n\nimport jgnash.convert.importat.DateFormat;\nimport jgnash.convert.importat.ImportBank;\nimport jgnash.convert.importat.qif.QifAccount;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.AccountType;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.TextResource;\n\n/**\n * Import Wizard, base account selection.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportPageOneController extends AbstractWizardPaneController<ImportWizard.Settings> {\n\n    private static final String DATE_FORMAT = \"dateFormat\";\n\n    @FXML\n    private Label dateFormatLabel;\n\n    @FXML\n    private ChoiceBox<DateFormat> dateFormatChoiceBox;\n\n    @FXML\n    private TextFlow textFlow;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private AccountComboBox accountComboBox;\n\n    private final SimpleBooleanProperty valid = new SimpleBooleanProperty(false);\n\n    private final SimpleBooleanProperty dateFormatSelectionEnabled = new SimpleBooleanProperty(false);\n\n    private final Preferences preferences = Preferences.userNodeForPackage(ImportPageOneController.class);\n\n    @FXML\n    private void initialize() {\n        textFlow.getChildren().addAll(new Text(TextResource.getString(\"ImportOne.txt\")));\n\n        valid.bind(accountComboBox.valueProperty().isNotNull());\n\n        updateDescriptor();\n\n        accountComboBox.valueProperty().addListener((observable, oldValue, newValue) -> updateDescriptor());\n\n        dateFormatChoiceBox.getItems().addAll(DateFormat.values());\n        dateFormatChoiceBox.getSelectionModel().select(preferences.getInt(DATE_FORMAT, 0));\n\n        // hide the controls if date format selection is not permitted\n        dateFormatChoiceBox.disableProperty().bind(dateFormatSelectionEnabled.not());\n        dateFormatChoiceBox.managedProperty().bind(dateFormatSelectionEnabled);\n        dateFormatLabel.managedProperty().bind(dateFormatSelectionEnabled);\n    }\n\n    @Override\n    public void putSettings(final Map<ImportWizard.Settings, Object> map) {\n        map.put(ImportWizard.Settings.ACCOUNT, accountComboBox.getValue());\n\n        if (!dateFormatChoiceBox.isDisabled()) {\n            map.put(ImportWizard.Settings.DATE_FORMAT, dateFormatChoiceBox.getValue());\n\n            preferences.putInt(DATE_FORMAT, dateFormatChoiceBox.getSelectionModel().getSelectedIndex());\n\n            final ImportBank<?> bank = (ImportBank<?>) map.get(ImportWizard.Settings.BANK);\n\n            if (bank instanceof QifAccount) {\n                ((QifAccount) bank).setDateFormat(dateFormatChoiceBox.getValue());\n            }\n        }\n    }\n\n    @Override\n    public void getSettings(final Map<ImportWizard.Settings, Object> map) {\n        final ImportBank<?> bank = (ImportBank<?>) map.get(ImportWizard.Settings.BANK);\n\n        // Filter the available account types to prevent import errors\n        if (bank != null && bank.isInvestmentAccount()) {\n            accountComboBox.setPredicate(AccountComboBox.getDefaultPredicate()\n                    .and(account -> account.getAccountType().getAccountGroup() == AccountGroup.INVEST));\n        } else {\n            accountComboBox.setPredicate(AccountComboBox.getDefaultPredicate()\n                    .and(account -> account.getAccountType() != AccountType.INCOME\n                            && account.getAccountType() != AccountType.EXPENSE));\n        }\n\n        if (map.get(ImportWizard.Settings.ACCOUNT) != null) {\n            accountComboBox.setAccountValue((Account) map.get(ImportWizard.Settings.ACCOUNT));\n        }\n\n        if (!dateFormatChoiceBox.isDisabled()) {\n            if (bank instanceof QifAccount) {\n                dateFormatChoiceBox.setValue(((QifAccount) bank).getDateFormat());\n            }\n        }\n\n        updateDescriptor();\n    }\n\n    @Override\n    public boolean isPaneValid() {\n        return valid.getValue();\n    }\n\n    @Override\n    public String toString() {\n        return \"1. \" + ResourceUtils.getString(\"Title.SelDestAccount\");\n    }\n\n    SimpleBooleanProperty dateFormatSelectionEnabled() {\n        return dateFormatSelectionEnabled;\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/imports/ImportPageThreeController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.imports;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ResourceBundle;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Label;\n\nimport jgnash.convert.importat.ImportState;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.engine.Account;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Import Wizard, base account selection.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportPageThreeController extends AbstractWizardPaneController<ImportWizard.Settings> {\n\n    @FXML\n    private Label destLabel;\n\n    @FXML\n    private Label transCountLabel;\n\n    @FXML\n    private ResourceBundle resources;\n\n    @FXML\n    private void initialize() {\n        updateDescriptor();\n    }\n\n    @Override\n    public void putSettings(final Map<ImportWizard.Settings, Object> map) {\n        // intentionally empty\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void getSettings(final Map<ImportWizard.Settings, Object> map) {\n        final Account account = (Account) map.get(ImportWizard.Settings.ACCOUNT);\n        final List<ImportTransaction> transactions = (List<ImportTransaction>) map.get(ImportWizard.Settings.TRANSACTIONS);\n\n        JavaFXUtils.runLater(() -> destLabel.setText(account.getName()));\n\n        final AtomicInteger count = new AtomicInteger();\n\n        transactions.stream().filter(tran -> tran.getState() == ImportState.NEW\n                || tran.getState() == ImportState.NOT_EQUAL)\n                .forEach(tran -> count.incrementAndGet());\n\n        JavaFXUtils.runLater(() -> transCountLabel.setText(Integer.toString(count.get())));\n\n        updateDescriptor();\n    }\n\n    @Override\n    public boolean isPaneValid() {\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return \"3. \" + ResourceUtils.getString(\"Title.ImpSum\");\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/imports/ImportPageTwoController.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.imports;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\n\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.beans.property.SimpleStringProperty;\nimport javafx.collections.FXCollections;\nimport javafx.collections.ListChangeListener;\nimport javafx.fxml.FXML;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.ContentDisplay;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TableCell;\nimport javafx.scene.control.TableColumn;\nimport javafx.scene.control.TableRow;\nimport javafx.scene.control.TableView;\nimport javafx.scene.control.Tooltip;\nimport javafx.scene.input.MouseEvent;\nimport javafx.scene.layout.StackPane;\nimport javafx.scene.text.Text;\nimport javafx.scene.text.TextFlow;\n\nimport jgnash.convert.importat.BayesImportClassifier;\nimport jgnash.convert.importat.GenericImport;\nimport jgnash.convert.importat.ImportBank;\nimport jgnash.convert.importat.ImportFilter;\nimport jgnash.convert.importat.ImportState;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.convert.importat.ImportUtils;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionType;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.resource.util.TextResource;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.BigDecimalTableCell;\nimport jgnash.uifx.control.ShortDateTableCell;\nimport jgnash.uifx.control.wizard.AbstractWizardPaneController;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.util.TableViewManager;\nimport jgnash.util.Nullable;\n\n/**\n * Import Wizard, imported transaction wizard.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportPageTwoController extends AbstractWizardPaneController<ImportWizard.Settings> {\n\n    private static final String PREF_NODE = \"/jgnash/uifx/wizard/imports\";\n\n    private static final double[] PREF_COLUMN_WEIGHTS = {0, 0, 0, 50, 50, 0, 0, 0, 0, 0};\n\n    private static final double[] MIN_COLUMN_WIDTHS = {0, 0, 0, 0, 0, 90, 90, 90, 0, 0};\n\n    private final SimpleBooleanProperty valid = new SimpleBooleanProperty(false);\n\n    private final NumberFormat numberFormat = NumberFormat.getNumberInstance();\n\n    private static final Account NOP_EXPENSE_ACCOUNT = new Account();\n\n    @FXML\n    private TextFlow textFlow;\n\n    @FXML\n    private TableView<ImportTransaction> tableView;\n\n    @FXML\n    private Button deleteButton;\n\n    @FXML\n    private ResourceBundle resources;\n\n    private TableViewManager<ImportTransaction> tableViewManager;\n\n    private TableColumn<ImportTransaction, Account> incomeAccountColumn;\n\n    private TableColumn<ImportTransaction, Account> expenseAccountColumn;\n\n    private TableColumn<ImportTransaction, String> typeColumn;\n\n    private Account baseAccount = null;\n\n    private Account lastAccount;\n\n    private Account lastGainsAccount;\n\n    private Account lastFeesAccount;\n\n    static {\n        NOP_EXPENSE_ACCOUNT.setName(\"…\");   // universal N/A for tabular data\n    }\n\n    @FXML\n    private void initialize() {\n        textFlow.getChildren().addAll(new Text(TextResource.getString(\"ImportTwo.txt\")));\n\n        deleteButton.disableProperty().bind(tableView.getSelectionModel().selectedItemProperty().isNull());\n\n        tableView.setTableMenuButtonVisible(false);\n        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);\n        tableView.setEditable(true);\n\n        tableView.getItems().addListener((ListChangeListener<ImportTransaction>) c ->\n                valid.set(tableView.getItems().size() > 0));\n\n        buildTableView();\n\n        tableViewManager = new TableViewManager<>(tableView, PREF_NODE);\n        tableViewManager.setColumnWeightFactory(column -> PREF_COLUMN_WEIGHTS[column]);\n        tableViewManager.setMinimumColumnWidthFactory(column -> MIN_COLUMN_WIDTHS[column]);\n\n        updateDescriptor();\n    }\n\n    private void buildTableView() {\n\n        final TableColumn<ImportTransaction, ImportState> stateColumn = new TableColumn<>();\n        stateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getState()));\n\n        stateColumn.setCellFactory(param -> {\n            TableCell<ImportTransaction, ImportState> cell =\n                    new ImportStateTableCell();\n\n            cell.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {\n                if (event.getClickCount() > 1) {\n                    final ImportTransaction t = tableView.getItems().get(((TableCell<?, ?>) event.getSource())\n                            .getTableRow().getIndex());\n\n                    switch (t.getState()) {\n                        case EQUAL:\n                            t.setState(ImportState.NOT_EQUAL);\n                            break;\n                        case NOT_EQUAL:\n                            t.setState(ImportState.EQUAL);\n                            break;\n                        case NEW:\n                            t.setState(ImportState.IGNORE);\n                            break;\n                        case IGNORE:\n                            t.setState(ImportState.NEW);\n                            break;\n                    }\n\n                    JavaFXUtils.runLater(tableView::refresh);\n                }\n            });\n            return cell;\n        });\n\n        tableView.getColumns().add(stateColumn);\n\n        final TableColumn<ImportTransaction, LocalDate> dateColumn =\n                new TableColumn<>(resources.getString(\"Column.Date\"));\n        dateColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getDatePosted()));\n        dateColumn.setCellFactory(param -> new ShortDateTableCell<>());\n        tableView.getColumns().add(dateColumn);\n\n        final TableColumn<ImportTransaction, String> numberColumn =\n                new TableColumn<>(resources.getString(\"Column.Num\"));\n        numberColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getCheckNumber()));\n        tableView.getColumns().add(numberColumn);\n\n        final TableColumn<ImportTransaction, String> payeeColumn =\n                new TableColumn<>(resources.getString(\"Column.Payee\"));\n        payeeColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getPayee()));\n        tableView.getColumns().add(payeeColumn);\n\n        final TableColumn<ImportTransaction, String> memoColumn =\n                new TableColumn<>(resources.getString(\"Column.Memo\"));\n        memoColumn.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().getMemo()));\n        tableView.getColumns().add(memoColumn);\n\n        final TableColumn<ImportTransaction, Account> accountColumn =\n                new TableColumn<>(resources.getString(\"Column.Account\"));\n\n        accountColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null && param.getValue().getAccount() != null) {\n                return new SimpleObjectProperty<>(param.getValue().getAccount());\n            }\n            return null;\n        });\n        accountColumn.setCellFactory(param -> new AccountComboBoxTableCell<>());\n        accountColumn.setEditable(true);\n\n        accountColumn.setOnEditCommit(event -> {\n            event.getTableView().getItems().get(event.getTablePosition().getRow()).setAccount(event.getNewValue());\n            lastAccount = event.getNewValue();\n            JavaFXUtils.runLater(tableViewManager::packTable);\n        });\n        tableView.getColumns().add(accountColumn);\n\n\n        incomeAccountColumn = new TableColumn<>(resources.getString(\"Column.Income\"));\n        incomeAccountColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null && param.getValue().getGainsAccount() != null) {\n                return new SimpleObjectProperty<>(param.getValue().getGainsAccount());\n            }\n            return null;\n        });\n        incomeAccountColumn.setCellFactory(param -> new IncomeAccountComboBoxTableCell<>());\n        incomeAccountColumn.setEditable(true);\n        incomeAccountColumn.setOnEditCommit(event -> {\n            event.getTableView().getItems().get(event.getTablePosition().getRow()).setGainsAccount(event.getNewValue());\n            lastGainsAccount = event.getNewValue();\n            JavaFXUtils.runLater(tableViewManager::packTable);\n        });\n        tableView.getColumns().add(incomeAccountColumn);\n\n        expenseAccountColumn = new TableColumn<>(resources.getString(\"Column.Expense\"));\n        expenseAccountColumn.setCellValueFactory(param -> {\n            if (param.getValue() != null && param.getValue().getFeesAccount() != null) {\n                if (param.getValue().getFees().compareTo(BigDecimal.ZERO) != 0) {\n                    return new SimpleObjectProperty<>(param.getValue().getFeesAccount());\n                }\n                \n\t\t\t\treturn new SimpleObjectProperty<>(NOP_EXPENSE_ACCOUNT);  // nop account\n            }\n            return null;\n        });\n        expenseAccountColumn.setCellFactory(param -> new ExpenseAccountComboBoxTableCell<>());\n        expenseAccountColumn.setEditable(true);\n        expenseAccountColumn.setOnEditCommit(event -> {\n            event.getTableView().getItems().get(event.getTablePosition().getRow()).setFeesAccount(event.getNewValue());\n            JavaFXUtils.runLater(tableViewManager::packTable);\n        });\n        tableView.getColumns().add(expenseAccountColumn);\n\n\n        final TableColumn<ImportTransaction, BigDecimal> amountColumn =\n                new TableColumn<>(resources.getString(\"Column.Amount\"));\n\n        amountColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getAmount()));\n        amountColumn.setCellFactory(param -> new BigDecimalTableCell<>(numberFormat));\n\n        amountColumn.setCellFactory(param -> {\n            final TableCell<ImportTransaction, BigDecimal> cell = new BigDecimalTableCell<>(numberFormat);\n\n            // add tool tip\n            cell.indexProperty().addListener((observable, oldValue, newValue) -> {\n                final int index = newValue.intValue();\n\n                if (index >= 0 && index < tableView.itemsProperty().get().size()) {\n                    cell.setTooltip(new Tooltip(tableView.itemsProperty().get().get(index).getToolTip()));\n                }\n            });\n\n            return cell;\n        });\n\n\n        tableView.getColumns().add(amountColumn);\n\n\n        typeColumn = new TableColumn<>(resources.getString(\"Column.Type\"));\n        typeColumn.setCellValueFactory(param -> {\n\n            TransactionType transactionType = TransactionType.SINGLENTRY;\n\n            if (param.getValue().isInvestmentTransaction()) {\n                transactionType = param.getValue().getTransactionType();\n            } else if (!param.getValue().getAccount().equals(baseAccount)) {\n                transactionType = TransactionType.DOUBLEENTRY;\n            }\n\n            return new SimpleStringProperty(transactionType.toString());\n        });\n        tableView.getColumns().add(typeColumn);\n    }\n\n    @Override\n    public void putSettings(final Map<ImportWizard.Settings, Object> map) {\n        map.put(ImportWizard.Settings.TRANSACTIONS, new ArrayList<>(tableView.getItems()));\n    }\n\n    @Override\n    public void getSettings(final Map<ImportWizard.Settings, Object> map) {\n\n        @SuppressWarnings(\"unchecked\")\n        final ImportBank<ImportTransaction> bank = (ImportBank<ImportTransaction>) map.get(ImportWizard.Settings.BANK);\n\n        if (bank != null) {\n            final List<ImportTransaction> list = bank.getTransactions();\n\n            baseAccount = (Account) map.get(ImportWizard.Settings.ACCOUNT);\n\n            final CurrencyNode currencyNode = baseAccount.getCurrencyNode();\n\n            // rescale for consistency\n            numberFormat.setMinimumFractionDigits(currencyNode.getScale());\n            numberFormat.setMaximumFractionDigits(currencyNode.getScale());\n\n            // List of enabled import filters\n            final List<ImportFilter> importFilterList = ImportFilter.getEnabledImportFilters();\n\n            // set to sane account assuming it's going to be a single entry\n            for (final ImportTransaction t : list) {\n\n                // Process transactions with the import filter\n                for (final ImportFilter importFilter : importFilterList) {\n                    importFilter.acceptTransaction(t);  // pass the import transaction for manipulation by the script\n                    t.setMemo(importFilter.processMemo(t.getMemo()));\n                    t.setPayee(importFilter.processPayee(t.getPayee()));\n                }\n\n                if (t.getTransactionType() != TransactionType.REINVESTDIV) {\n                    t.setAccount(baseAccount);\n                }\n\n                if (t.isInvestmentTransaction()) {\n                    switch (t.getTransactionType()) {\n                        case BUYSHARE:\n                            t.setFeesAccount(baseAccount);\n                            break;\n                        case SELLSHARE:\n                        case REINVESTDIV:\n                            t.setFeesAccount(baseAccount);\n                            t.setGainsAccount(baseAccount);\n                            break;\n                        case DIVIDEND:\n                            t.setGainsAccount(baseAccount);\n                            break;\n                        default:\n                    }\n                }\n\n                t.setState(ImportState.NEW);  // force reset\n            }\n\n            incomeAccountColumn.setVisible(bank.isInvestmentAccount());\n            expenseAccountColumn.setVisible(bank.isInvestmentAccount());\n            typeColumn.setVisible(bank.isInvestmentAccount());\n\n            // match up any pre-existing transactions\n            GenericImport.matchTransactions(list, baseAccount);\n\n            // classify the transactions\n            if (Options.globalBayesProperty().get()) {\n                final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n                Objects.requireNonNull(engine);\n\n                final List<Transaction> transactions = engine.getTransactions();\n                transactions.sort(null);\n\n                BayesImportClassifier.classifyTransactions(list, transactions, baseAccount);\n            } else {\n                BayesImportClassifier.classifyTransactions(list, baseAccount.getSortedTransactionList(), baseAccount);\n            }\n\n            // override the classifier if an account has been specified already\n            for (final ImportTransaction importTransaction : list) {\n                final Account account = ImportUtils.matchAccount(importTransaction);\n\n                if (account != null) {\n                    importTransaction.setAccount(account);\n                }\n            }\n\n            tableView.getItems().setAll(list);\n            FXCollections.sort(tableView.getItems());\n\n            tableViewManager.restoreLayout();\n        }\n\n        JavaFXUtils.runLater(tableViewManager::packTable);\n\n        updateDescriptor();\n    }\n\n    @Override\n    public boolean isPaneValid() {\n        return valid.getValue();\n    }\n\n    @Override\n    public String toString() {\n        return \"2. \" + ResourceUtils.getString(\"Title.ModImportTrans\");\n    }\n\n    @FXML\n    private void handleDeleteAction() {\n        tableView.getItems().removeAll(tableView.getSelectionModel().getSelectedItems());\n    }\n\n    private static class ImportStateTableCell extends TableCell<ImportTransaction, ImportState> {\n        @Override\n        public void updateItem(final ImportState item, final boolean empty) {\n            super.updateItem(item, empty);\n\n            if (empty) {\n                setText(null);\n                setGraphic(null);\n            } else if (item != null) {\n                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);\n                setText(null);\n\n                switch (item) {\n                    case IGNORE:\n                        setGraphic(new StackPane(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.MINUS_CIRCLE)));\n                        break;\n                    case NEW:\n                    case NOT_EQUAL:\n                        setGraphic(new StackPane(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.PLUS_CIRCLE)));\n                        break;\n                    case EQUAL:\n                        setGraphic(new StackPane(new Label(\"=\")));\n                        break;\n                }\n            }\n        }\n    }\n\n    class AccountComboBoxTableCell<S> extends TableCell<S, Account> {\n\n        private final AccountComboBox comboBox;\n\n        private boolean firstPassEdit = false;\n\n        AccountComboBoxTableCell() {\n            this.getStyleClass().add(\"combo-box-table-cell\");\n\n            comboBox = new AccountComboBox();\n\n            comboBox.setMaxWidth(Double.MAX_VALUE);\n\n            comboBox.setOnHidden(event -> {\n                if (isEditing()) {\n                    firstPassEdit = true;\n                    lastAccount = comboBox.getValue();\n                    commitEdit(comboBox.getValue());\n                }\n            });\n        }\n\n        @Override\n        public void startEdit() {\n            final TableRow<?> row = getTableRow();\n\n            if (row != null) {\n                final ImportTransaction importTransaction = (ImportTransaction) row.getItem();\n\n                if (!importTransaction.isInvestmentTransaction()) {\n                    editableProperty().setValue(true);\n                } else {    // reinvested dividends do not have a cash account\n                    editableProperty().setValue(importTransaction.getTransactionType() != TransactionType.REINVESTDIV);\n                }\n            }\n\n            if (!isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) {\n                return;\n            }\n\n            if (!firstPassEdit && lastAccount != null) {\n                comboBox.getSelectionModel().select(lastAccount);\n            } else {\n                comboBox.getSelectionModel().select(getItem());\n            }\n\n            super.startEdit();\n            setText(null);\n            setGraphic(comboBox);\n        }\n\n        @Override\n        public void cancelEdit() {\n            super.cancelEdit();\n\n            if (getItem() != null) {\n                setText(getItem().getName());\n            }\n\n            setGraphic(null);\n        }\n\n        @Override\n        public void updateItem(@Nullable final Account item, final boolean empty) {\n            super.updateItem(item, empty);\n\n            if (empty || item == null) {\n                setText(null);\n                setGraphic(null);\n            } else {\n                if (isEditing()) {\n                    comboBox.getSelectionModel().select(getItem());\n                    setText(null);\n                    setGraphic(comboBox);\n                } else {\n                    setText(getItem().getName());\n                    setGraphic(null);\n                }\n            }\n        }\n    }\n\n    class IncomeAccountComboBoxTableCell<S extends ImportTransaction> extends TableCell<S, Account> {\n\n        private final AccountComboBox comboBox;\n\n        private boolean firstPassEdit = false;\n\n        IncomeAccountComboBoxTableCell() {\n            this.getStyleClass().add(\"combo-box-table-cell\");\n\n            comboBox = new AccountComboBox();\n\n            comboBox.setPredicate(AccountComboBox.getDefaultPredicate()\n                    .and(account -> account.getAccountType() == AccountType.INCOME || account == baseAccount));\n\n            comboBox.setMaxWidth(Double.MAX_VALUE);\n\n            comboBox.setOnHidden(event -> {\n                if (isEditing()) {\n                    firstPassEdit = true;\n                    lastGainsAccount = comboBox.getValue();\n                    commitEdit(comboBox.getValue());\n                }\n            });\n        }\n\n        @Override\n        public void startEdit() {\n            final TableRow<?> row = getTableRow();\n\n            if (row != null) {\n                final ImportTransaction importTransaction = (ImportTransaction) row.getItem();\n\n                editableProperty().setValue(importTransaction.getTransactionType() == TransactionType.SELLSHARE\n                        || importTransaction.getTransactionType() == TransactionType.DIVIDEND\n                        || importTransaction.getTransactionType() == TransactionType.REINVESTDIV);\n            }\n\n            if (!isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) {\n                return;\n            }\n\n            if (!firstPassEdit && lastGainsAccount != null) {\n                comboBox.getSelectionModel().select(lastGainsAccount);\n            } else {\n                comboBox.getSelectionModel().select(getItem());\n            }\n\n            super.startEdit();\n            setText(null);\n            setGraphic(comboBox);\n        }\n\n        @Override\n        public void cancelEdit() {\n            super.cancelEdit();\n\n            if (getItem() != null) {\n                setText(getItem().getName());\n            }\n\n            setGraphic(null);\n        }\n\n        @Override\n        public void updateItem(@Nullable final Account item, final boolean empty) {\n            super.updateItem(item, empty);\n\n            TableRow<?> row = getTableRow();\n\n            if (empty || item == null || row == null) {\n                setText(null);\n                setGraphic(null);\n            } else {\n                if (isEditing()) {\n                    comboBox.getSelectionModel().select(getItem());\n                    setText(null);\n                    setGraphic(comboBox);\n                } else {\n                    setText(getItem().getName());\n                    setGraphic(null);\n                }\n            }\n        }\n    }\n\n    class ExpenseAccountComboBoxTableCell<S extends ImportTransaction> extends TableCell<S, Account> {\n\n        private final AccountComboBox comboBox;\n\n        private boolean firstPassEdit = false;\n\n        ExpenseAccountComboBoxTableCell() {\n            this.getStyleClass().add(\"combo-box-table-cell\");\n\n            comboBox = new AccountComboBox();\n            comboBox.getUnfilteredItems().addAll(NOP_EXPENSE_ACCOUNT);\n            comboBox.setPredicate(AccountComboBox.getDefaultPredicate()\n                    .and(account -> account.getAccountType() == AccountType.EXPENSE || account == baseAccount\n                            || account == NOP_EXPENSE_ACCOUNT));\n\n            comboBox.setMaxWidth(Double.MAX_VALUE);\n\n            comboBox.setOnHidden(event -> {\n                if (isEditing()) {\n                    firstPassEdit = true;\n                    lastFeesAccount = comboBox.getValue();\n                    commitEdit(comboBox.getValue());\n                }\n            });\n        }\n\n        @Override\n        public void startEdit() {\n            final TableRow<?> row = getTableRow();\n\n            if (row != null) {\n                final ImportTransaction importTransaction = (ImportTransaction) row.getItem();\n\n                editableProperty().setValue(importTransaction.getFees().compareTo(BigDecimal.ZERO) != 0\n                        && (importTransaction.getTransactionType() == TransactionType.SELLSHARE\n                        || importTransaction.getTransactionType() == TransactionType.BUYSHARE\n                        || importTransaction.getTransactionType() == TransactionType.REINVESTDIV));\n\n            }\n\n            if (!isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) {\n                return;\n            }\n\n            if (!firstPassEdit && lastFeesAccount != null) {\n                comboBox.getSelectionModel().select(lastFeesAccount);\n            } else {\n                comboBox.getSelectionModel().select(getItem());\n            }\n\n            super.startEdit();\n            setText(null);\n            setGraphic(comboBox);\n        }\n\n        @Override\n        public void cancelEdit() {\n            super.cancelEdit();\n\n            if (getItem() != null) {\n                setText(getItem().getName());\n            }\n\n            setGraphic(null);\n        }\n\n        @Override\n        public void updateItem(@Nullable final Account item, final boolean empty) {\n            super.updateItem(item, empty);\n\n            TableRow<?> row = getTableRow();\n\n            if (empty || item == null || row == null) {\n                setText(null);\n                setGraphic(null);\n            } else {\n                if (isEditing()) {\n                    comboBox.getSelectionModel().select(getItem());\n                    setText(null);\n                    setGraphic(comboBox);\n                } else {\n                    setText(getItem().getName());\n                    setGraphic(null);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/java/jgnash/uifx/wizard/imports/ImportWizard.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.wizard.imports;\n\nimport java.io.IOException;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleBooleanProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.fxml.FXMLLoader;\nimport javafx.scene.layout.Pane;\nimport javafx.stage.Stage;\n\nimport jgnash.convert.importat.GenericImport;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.control.wizard.WizardDialogController;\nimport jgnash.uifx.util.FXMLUtils;\nimport jgnash.uifx.util.JavaFXUtils;\n\n/**\n * Import Wizard Dialog.\n *\n * @author Craig Cavanaugh\n */\npublic class ImportWizard {\n\n    public enum Settings {\n        BANK,\n        ACCOUNT,\n        TRANSACTIONS,\n        DATE_FORMAT\n    }\n\n    private final ObjectProperty<WizardDialogController<Settings>> wizardController = new SimpleObjectProperty<>();\n\n    private final SimpleBooleanProperty dateFormatSelectionEnabled = new SimpleBooleanProperty(false);\n\n    private final Stage stage;\n\n    public ImportWizard() {\n\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final FXMLUtils.Pair<WizardDialogController<Settings>> pair =\n                FXMLUtils.load(WizardDialogController.class.getResource(\"WizardDialog.fxml\"),\n                        resources.getString(\"Title.ImportTransactions\"));\n\n        stage = pair.getStage();\n        wizardControllerProperty().set(pair.getController());\n\n        final WizardDialogController<Settings> wizardController = wizardControllerProperty().get();\n\n        // force a default account before loading tasks to prevent NPE.  The ordered pages will sort out better choices\n        wizardController.setSetting(Settings.ACCOUNT, GenericImport.findFirstAvailableAccount());\n\n        try {\n            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(\"ImportPageOne.fxml\"), resources);\n            Pane pane = fxmlLoader.load();\n            ImportPageOneController importPageOneController = fxmlLoader.getController();\n            wizardController.addTaskPane(importPageOneController, pane);\n            importPageOneController.dateFormatSelectionEnabled().bind(dateFormatSelectionEnabled());\n\n            fxmlLoader = new FXMLLoader(getClass().getResource(\"ImportPageTwo.fxml\"), resources);\n            pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n            fxmlLoader = new FXMLLoader(getClass().getResource(\"ImportPageThree.fxml\"), resources);\n            pane = fxmlLoader.load();\n            wizardController.addTaskPane(fxmlLoader.getController(), pane);\n\n        } catch (final IOException ioe) {\n            Logger.getLogger(ImportWizard.class.getName()).log(Level.SEVERE, ioe.getLocalizedMessage(), ioe);\n        }\n\n        JavaFXUtils.runLater(() -> {\n\n            stage.sizeToScene();\n\n            stage.setMinWidth(stage.getWidth());\n            stage.setMinHeight(stage.getHeight());\n        });\n    }\n\n    public ObjectProperty<WizardDialogController<Settings>> wizardControllerProperty() {\n        return wizardController;\n    }\n\n    public SimpleBooleanProperty dateFormatSelectionEnabled() {\n        return dateFormatSelectionEnabled;\n    }\n\n    public void showAndWait() {\n       stage.showAndWait();\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nApplication-Name: jGnashFx\nJavaFX-Application-Class: jGnashFx\nMain-Class: jgnash.app.jGnash\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/skin/default.css",
    "content": "/*\n * Reduce the overall font size of the application\n\n  Windows 12px -> em units    -> Mac 13px      |\n  ----------------------------------------\n       1px     -> 0.083333em  -> 1.08px ~ 2px\n       2px     -> 0.166667em  -> 2.16px ~ 3px\n       3px  = 0.25em\n       4px  = 0.333333em\n       5px  = 0.416667em\n       6px  = 0.5em\n       7px  = 0.583333em\n       8px  = 0.666667em\n       9px  = 0.75em\n      10px  = 0.833333em\n      11px  = 0.916667em\n      12px  = 1em\n\n      -fx-control-inner-background-alt is not defined for caspian\n */\n.root {\n    /* Hushes up Caspian without effecting Modena, the default is 2% */\n    /*noinspection CssInvalidFunction*/\n    -fx-control-inner-background-alt: derive(-fx-control-inner-background, -6%);\n}\n\n.text {\n    -fx-font-smoothing-type: gray;\n}\n\n.default-color0.chart-bar { -fx-bar-fill: CHART_COLOR_3; }\n.default-color1.chart-bar { -fx-bar-fill: CHART_COLOR_1; }\n.default-color2.chart-bar { -fx-bar-fill: CHART_COLOR_2; }\n\n.chart-content {\n    -fx-padding: 5px;\n}\n\n.chart-pie-label {\n    -fx-padding: 3px;\n}\n\n.pie-legend-symbol.chart-pie {\n    -fx-padding: 4px;\n}\n\n/* ==== Hides the focus around tables ==== */\n.table-view:focused,\n.tree-table-view:focused {\n    -fx-background-color: -fx-box-border, -fx-control-inner-background;\n    -fx-background-insets: 0, 1;\n    -fx-padding: 1;\n    -fx-background-radius: 0, 0;\n}\n\n/*.table-row-cell {\n    -fx-cell-size: 1.75em;\n}\n\n.tree-table-row-cell {\n    -fx-cell-size: 1.75em;\n}*/\n\n/* ==== Ensure we have odd/even background changes ==== */\n.tree-table-row-cell:odd {\n    -fx-background: -fx-control-inner-background-alt;\n}\n\n.tree-table-row-cell:odd:focused {\n    -fx-background: -fx-selection-bar;\n}\n\n/* ==== Adjust margins of the status label for improved appearance ==== */\n.status-label {\n    -fx-graphic-text-gap: 0.833333em;\n    -fx-label-padding: 0 0 0 0.25em;\n}\n\n.status-bar {\n    -fx-padding: 4px;\n    -fx-pref-height: 30px;\n    -fx-background-color: lightgray, -fx-body-color;\n    -fx-background-insets: 0, 1\n}\n\n.view-title {\n    -fx-font-weight: bold;\n    -fx-padding: 6 6 6 6;\n}\n\n.list-title {\n    -fx-font-weight: bold;\n}\n\n.list-button {\n    -fx-padding: 0.333333em 2.0em 0.333333em 2.0em;\n}\n\n.dialog {\n    -fx-padding: 1.2em;\n}\n\n.form {\n    -fx-hgap: 0.5em;\n    -fx-vgap: 0.666666em;\n    -fx-spacing: 0.5em;\n}\n\n.border {\n    -fx-border-color: -fx-box-border;\n    -fx-border-width: 1;\n    -fx-border-style: solid;\n}\n\n.info-bar {\n    -fx-hgap: 0.5em;\n    -fx-spacing: 0.5em;\n    -fx-padding: 0.3333em;\n    -fx-font-weight: bold;\n}\n\n.summary-bar {\n    -fx-padding: 0.3333em 0.75em 0.3333em 0.75em; /* 4 9 4 9 */\n    -fx-background-color: -fx-box-border, -fx-inner-border, -fx-body-color;\n    -fx-background-insets: 0, 0 1 1 1, 1 2 2 1;\n    -fx-font-weight: bold;\n    -fx-size: 2em;\n    -fx-text-fill: -fx-selection-bar-text;\n}\n\n/* ==== jGnash Pop over buttons ==== */\n.pop-over-button .arrow-button {\n    -fx-padding: 0;\n}\n.pop-over-button .arrow-button > .arrow {\n    -fx-padding: 0;\n}\n.pop-over-button .menu-item:focused {\n    -fx-background-color: transparent;\n}\n.pop-over-button .menu-item:focused .label {\n    -fx-text-fill: -fx-text-base-color;\n}\n.pop-over-button .context-menu {\n    -fx-background-color: -fx-background;\n    -fx-border-color: -fx-box-border;\n}\n\n/* ==== Hidden column headers ==== */\n.hidden-column-header .column-header-background {\n    visibility: hidden;\n    -fx-padding: -2em;\n    -fx-size: 0;\n}\n\n/* ==== Hide row focus for tables ==== */\n.hidden-row-focus .table-row-cell {\n    -fx-background-insets: 0, 0 0 0 0;\n}\n\n#disabled-cell {\n    -fx-opacity: 0.4;\n    -fx-font-weight: bold;\n}\n\n#enabled-cell {\n    -fx-opacity: 1.0;\n    -fx-font-weight: normal;\n}\n\n#tag-box {\n    -fx-hgap: 1.0em;\n    -fx-vgap: 0.666666em;\n    -fx-padding: 1.2em;\n}\n\n#tag-pane {\n    -fx-spacing: 1.0em;\n}\n\n#italic-label {\n    -fx-font-style: italic;\n}\n\n#italic-negative-label {\n    -fx-font-style: italic;\n    -fx-text-fill: darkred;\n}\n\n#normal-label {\n    -fx-font-style: normal;\n}\n\n#today-normal-label {\n    -fx-font-style: normal;\n    -fx-background-color: -fx-selection-bar;\n}\n\n#normal-negative-label {\n    -fx-font-style: normal;\n    -fx-text-fill: darkred;\n}\n\n#today-normal-negative-label {\n    -fx-font-style: normal;\n    -fx-text-fill: darkred;\n    -fx-background-color: -fx-selection-bar;\n}\n\n#bold-label {\n    -fx-font-style: normal;\n    -fx-font-weight: bold;\n}\n\n#today-bold-label {\n    -fx-font-style: normal;\n    -fx-font-weight: bold;\n    -fx-background-color: -fx-selection-bar;\n}\n\n#bold-negative-label {\n    -fx-font-style: normal;\n    -fx-text-fill: darkred;\n    -fx-font-weight: bold;\n}\n\n#today-bold-negative-label {\n    -fx-font-style: normal;\n    -fx-text-fill: darkred;\n    -fx-font-weight: bold;\n    -fx-background-color: -fx-selection-bar;\n}\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/skin/tableHideHorizontalScrollBar.css",
    "content": "/*noinspection ALL*/\n*.scroll-bar:horizontal *.increment-button,\n*.scroll-bar:horizontal *.decrement-button,\n*.scroll-bar:horizontal *.increment-arrow,\n*.scroll-bar:horizontal *.decrement-arrow {\n    visibility: hidden;\n    -fx-background-color: null;\n    -fx-background-radius: 0;\n    -fx-background-insets: 0;\n    -fx-padding: 0;\n}\n\n.scroll-bar:horizontal {\n    visibility: hidden;\n    -fx-opacity: 0;\n    -fx-padding: 0;\n}\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/skin/tableHideVerticalScrollBar.css",
    "content": "/*noinspection ALL*/\n.tree-view *.scroll-bar:vertical *.increment-button,\n.tree-view *.scroll-bar:vertical *.decrement-button,\n.tree-view *.scroll-bar:vertical *.increment-arrow,\n.tree-view *.scroll-bar:vertical *.decrement-arrow,\n.table-view *.scroll-bar:vertical *.increment-button,\n.table-view *.scroll-bar:vertical *.decrement-button,\n.table-view *.scroll-bar:vertical *.increment-arrow,\n.table-view *.scroll-bar:vertical *.decrement-arrow,\n.tree-table-view *.scroll-bar:vertical *.increment-button,\n.tree-table-view *.scroll-bar:vertical *.decrement-button,\n.tree-table-view *.scroll-bar:vertical *.increment-arrow,\n.tree-table-view *.scroll-bar:vertical *.decrement-arrow {\n     visibility: hidden;\n     -fx-background-color: null;\n     -fx-background-radius: 0;\n     -fx-background-insets: 0;\n     -fx-padding: 0;\n}\n\n.scroll-bar:vertical {\n     visibility: hidden;\n     -fx-opacity: 0;\n     -fx-padding: 0;\n}\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/about/AboutDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.control.Button?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n          fx:controller=\"jgnash.uifx.about.AboutDialogController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <TabPane fx:id=\"tabbedPane\" tabClosingPolicy=\"UNAVAILABLE\"/>\n    <ButtonBar GridPane.rowIndex=\"1\">\n        <buttons>\n            <Button text=\"%Button.Close\" onAction=\"#handleCloseAction\" ButtonBar.buttonData=\"CANCEL_CLOSE\" defaultButton=\"true\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/AlertDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" styleClass=\"form, dialog\">\n  <columnConstraints>\n    <ColumnConstraints hgrow=\"ALWAYS\"/>\n  </columnConstraints>\n  <rowConstraints>\n    <RowConstraints vgrow=\"ALWAYS\" valignment=\"TOP\"/>\n    <RowConstraints vgrow=\"NEVER\"/>\n  </rowConstraints>\n  <Label fx:id=\"message\" text=\"No Content\" wrapText=\"true\" minWidth=\"300\" GridPane.fillHeight=\"true\" graphicTextGap=\"15\"/>\n  <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"1\"/>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/ChoiceDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label fx:id=\"message\" text=\"No Content\" wrapText=\"true\" minWidth=\"300\" maxWidth=\"400\" graphicTextGap=\"15\"/>\n    <ComboBox fx:id=\"comboBox\" GridPane.rowIndex=\"1\"  minWidth=\"350\" maxWidth=\"Infinity\"/>\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"2\" >\n        <buttons>\n            <Button fx:id=\"okButton\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button fx:id=\"cancelButton\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/DateRangeDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<BorderPane fx:controller=\"jgnash.uifx.control.DateRangeDialogController\"\n            xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog\">\n    <center>\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.StartDate\"/>\n            <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.EndDate\" GridPane.rowIndex=\"1\"/>\n            <DatePickerEx fx:id=\"endDatePicker\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n            <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"2\" GridPane.columnSpan=\"2\">\n                <buttons>\n                    <Button onAction=\"#handleOkAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n                    <Button onAction=\"#handleCloseAction\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/DetailedDecimalTextField.fxml",
    "content": "<fx:root type=\"javafx.scene.control.ComboBoxBase\" xmlns:fx=\"http://javafx.com/fxml\">\n</fx:root>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/ExceptionDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\" styleClass=\"form, dialog\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label fx:id=\"message\" text=\"Message\" graphicTextGap=\"15\"/>\n    <TextArea fx:id=\"textArea\" minHeight=\"250.0\" minWidth=\"480.0\" maxHeight=\"250\" maxWidth=\"480\" editable=\"false\"\n              wrapText=\"true\" GridPane.fillHeight=\"true\" GridPane.fillWidth=\"true\" GridPane.rowIndex=\"1\"/>\n    <ButtonBar GridPane.rowIndex=\"2\">\n      <buttons>\n          <Button fx:id=\"clipboardButton\" text=\"%Button.CopyToClip\" ButtonBar.buttonData=\"LEFT\"/>\n          <Button ButtonBar.buttonData=\"BIG_GAP\" visible=\"false\"/>\n          <Button fx:id=\"closeButton\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n      </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/TabViewPane.fxml",
    "content": "<fx:root type=\"javafx.scene.control.TabPane\" xmlns:fx=\"http://javafx.com/fxml\">\n</fx:root>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/TextInputDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.TextFieldEx?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label fx:id=\"message\" text=\"No Content\" wrapText=\"true\" minWidth=\"150\" graphicTextGap=\"15\"/>\n    <TextFieldEx fx:id=\"textField\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\" />\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"1\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button fx:id=\"okButton\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button fx:id=\"cancelButton\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/control/wizard/WizardDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane maxHeight=\"Infinity\" maxWidth=\"Infinity\" prefHeight=\"480.0\" prefWidth=\"750.0\"\n          xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\"\n          fx:controller=\"jgnash.uifx.control.wizard.WizardDialogController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"400\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"ALWAYS\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <TitledPane animated=\"false\" collapsible=\"false\" maxHeight=\"Infinity\" text=\"%Title.Steps\">\n        <ListView fx:id=\"taskList\"/>\n    </TitledPane>\n    <TitledPane fx:id=\"taskTitlePane\" animated=\"false\" collapsible=\"false\" maxHeight=\"Infinity\" text=\"%Title.Steps\"\n                GridPane.columnIndex=\"1\" GridPane.fillHeight=\"true\">\n        <StackPane fx:id=\"taskPane\" prefHeight=\"150.0\" prefWidth=\"200.0\"/>\n    </TitledPane>\n    <ButtonBar GridPane.columnSpan=\"2\" GridPane.rowIndex=\"1\">\n      <buttons>\n          <Button fx:id=\"backButton\" onAction=\"#handleBackAction\" mnemonicParsing=\"false\" text=\"%Button.Back\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"CHEVRON_LEFT\"/>\n              </graphic>\n          </Button>\n          <Button fx:id=\"nextButton\" onAction=\"#handleNextAction\" mnemonicParsing=\"false\" text=\"%Button.Next\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"CHEVRON_RIGHT\"/>\n              </graphic>\n          </Button>\n          <Button ButtonBar.buttonData=\"SMALL_GAP\" visible=\"false\"/>\n          <Button fx:id=\"finishButton\" onAction=\"#handleFinishAction\" mnemonicParsing=\"false\" text=\"%Button.Finish\"/>\n          <Button fx:id=\"cancelButton\" onAction=\"#handleCancelAction\" mnemonicParsing=\"false\" text=\"%Button.Cancel\"/>\n      </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/ChangePasswordDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.PasswordField?>\n<?import javafx.scene.control.TitledPane?>\n\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.dialog.ChangeDatabasePasswordDialogController\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog\">\n    <center>\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.DatabaseName\"/>\n            <TextField fx:id=\"databaseTextField\" editable=\"false\" GridPane.columnIndex=\"1\"/>\n            <Button onAction=\"#handleDatabaseButtonAction\" GridPane.columnIndex=\"2\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"ELLIPSIS_H\"/>\n                </graphic>\n            </Button>\n\n            <Label text=\"%Label.Password\" GridPane.rowIndex=\"1\"/>\n            <PasswordField fx:id=\"passwordField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"/>\n\n            <TitledPane text=\"%Title.NewPassword\" collapsible=\"false\" GridPane.rowIndex=\"2\" GridPane.columnSpan=\"3\">\n                <GridPane styleClass=\"form\">\n                    <columnConstraints>\n                        <ColumnConstraints hgrow=\"NEVER\"/>\n                        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\"/>\n                    </columnConstraints>\n                    <rowConstraints>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                    </rowConstraints>\n\n                    <Label text=\"%Label.NewPassword\"/>\n                    <PasswordField fx:id=\"newPasswordField\" GridPane.columnIndex=\"1\"/>\n\n                    <Label text=\"%Label.VerifyPassword\" GridPane.rowIndex=\"1\"/>\n                    <PasswordField fx:id=\"verifyPasswordField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n                </GridPane>\n            </TitledPane>\n\n            <ButtonBar GridPane.rowIndex=\"3\" GridPane.columnSpan=\"3\">\n                <buttons>\n                    <Button fx:id=\"okayButton\" onAction=\"#handleOkAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n                    <Button onAction=\"#handleCloseAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/ImportScriptsDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" minHeight=\"-Infinity\"\n          fx:controller=\"jgnash.uifx.dialog.ImportScriptsDialogController\" styleClass=\"dialog, form\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"200.0\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <TableViewEx fx:id=\"tableView\"/>\n\n    <VBox GridPane.columnIndex=\"1\" styleClass=\"form\">\n        <Button fx:id=\"upButton\" onAction=\"#handleUpAction\" contentDisplay=\"GRAPHIC_ONLY\" minWidth=\"50\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LEVEL_UP\"/>\n            </graphic>\n        </Button>\n        <Button fx:id=\"downButton\" onAction=\"#handleDownAction\" contentDisplay=\"GRAPHIC_ONLY\" minWidth=\"50\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LEVEL_DOWN\"/>\n            </graphic>\n        </Button>\n    </VBox>\n\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"1\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button text=\"%Button.Ok\" onAction=\"#handleOkayCloseAction\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button text=\"%Button.Cancel\" onAction=\"#handleCloseAction\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/PackDatabaseDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.PasswordField?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.dialog.PackDatabaseDialogController\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog\">\n    <center>\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.DatabaseName\"/>\n            <TextField fx:id=\"databaseTextField\" editable=\"false\" GridPane.columnIndex=\"1\"/>\n            <Button onAction=\"#handleDatabaseButtonAction\" GridPane.columnIndex=\"2\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"ELLIPSIS_H\"/>\n                </graphic>\n            </Button>\n\n            <Label text=\"%Label.Password\" GridPane.rowIndex=\"1\"/>\n            <PasswordField fx:id=\"passwordField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"/>\n\n            <ButtonBar GridPane.rowIndex=\"2\" GridPane.columnSpan=\"3\">\n                <buttons>\n                    <Button fx:id=\"okayButton\" onAction=\"#handleOkAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n                    <Button onAction=\"#handleCloseAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/RemoteConnectionDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.PasswordField?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.IntegerTextField?>\n\n<BorderPane fx:controller=\"jgnash.uifx.dialog.RemoteConnectionDialogController\"\n            xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog\">\n    <center>\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.DatabaseServer\"/>\n            <TextField fx:id=\"hostTextField\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.Port\" GridPane.rowIndex=\"1\"/>\n            <IntegerTextField fx:id=\"portTextField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.Password\" GridPane.rowIndex=\"2\"/>\n            <PasswordField fx:id=\"passwordField\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n\n            <ButtonBar GridPane.rowIndex=\"3\" GridPane.columnSpan=\"3\">\n                <buttons>\n                    <Button fx:id=\"okayButton\" onAction=\"#handleOkAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n                    <Button onAction=\"#handleCloseAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/TagManagerDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ColorPicker?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.TextFieldEx?>\n\n<GridPane fx:controller=\"jgnash.uifx.dialog.TagManagerDialogController\" minHeight=\"300\" minWidth=\"-Infinity\"\n          xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"SOMETIMES\" prefWidth=\"110\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\" valignment=\"TOP\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <ButtonBar GridPane.halignment=\"LEFT\">\n        <buttons>\n            <Button text=\"%Button.New\" onAction=\"#handleNewAction\" ButtonBar.buttonData=\"LEFT\"\n                    ButtonBar.buttonUniformSize=\"true\"/>\n            <Button fx:id=\"duplicateButton\" text=\"%Button.Duplicate\" onAction=\"#handleDuplicateAction\"\n                    ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"true\"/>\n            <Button fx:id=\"deleteButton\" text=\"%Button.Delete\" onAction=\"#handleDeleteAction\"\n                    ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"true\"/>\n            <Button ButtonBar.buttonData=\"SMALL_GAP\" visible=\"false\"/>\n        </buttons>\n    </ButtonBar>\n\n    <ListView fx:id=\"tagListView\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\" GridPane.rowSpan=\"5\"\n              prefHeight=\"280\" minWidth=\"-Infinity\"/>\n\n    <Label text=\"%Label.Name\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"/>\n    <TextFieldEx fx:id=\"nameField\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"0\"/>\n\n    <Label text=\"%Label.Color\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n    <ColorPicker fx:id=\"colorPicker\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n\n    <Label text=\"%Label.Icon\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n    <ComboBox fx:id=\"iconCombo\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\" GridPane.fillWidth=\"true\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.Description\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\"/>\n    <TextArea fx:id=\"descriptionTextArea\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\" GridPane.rowIndex=\"4\"/>\n\n    <ButtonBar GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\" GridPane.rowIndex=\"5\">\n        <buttons>\n            <Button fx:id=\"saveButton\" mnemonicParsing=\"false\" onAction=\"#handleSaveAction\" text=\"%Button.Save\"\n                    ButtonBar.buttonData=\"APPLY\" maxWidth=\"Infinity\"/>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleResetAction\" text=\"%Button.Cancel\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n        </buttons>\n    </ButtonBar>\n\n    <ButtonBar GridPane.columnIndex=\"0\" GridPane.rowIndex=\"7\" GridPane.columnSpan=\"3\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/currency/AddRemoveCurrency.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" minHeight=\"-Infinity\"\n          fx:controller=\"jgnash.uifx.dialog.currency.AddRemoveCurrencyController\" styleClass=\"dialog, form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <GridPane GridPane.rowIndex=\"0\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"80.0\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints minHeight=\"100\" vgrow=\"ALWAYS\"/>\n        </rowConstraints>\n\n        <TitledPane text=\"%Title.Available\" collapsible=\"false\" minHeight=\"180\" maxHeight=\"Infinity\"\n                    GridPane.fillHeight=\"true\">\n            <ListView fx:id=\"availableList\" prefHeight=\"180\"/>\n        </TitledPane>\n\n        <VBox GridPane.columnIndex=\"1\" styleClass=\"form\" GridPane.fillHeight=\"true\">\n            <Button text=\"%Button.Add\" onAction=\"#handleAddAction\" contentDisplay=\"RIGHT\" maxWidth=\"Infinity\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHEVRON_RIGHT\"/>\n                </graphic>\n            </Button>\n            <Button text=\"%Button.Remove\" onAction=\"#handleRemoveAction\"\n                    maxWidth=\"Infinity\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHEVRON_LEFT\"/>\n                </graphic>\n            </Button>\n        </VBox>\n\n        <TitledPane text=\"%Title.Selected\" collapsible=\"false\" minHeight=\"180\" maxHeight=\"Infinity\"\n                    GridPane.fillHeight=\"true\" GridPane.columnIndex=\"2\">\n            <ListView fx:id=\"selectedList\" prefHeight=\"180\"/>\n        </TitledPane>\n    </GridPane>\n\n    <GridPane GridPane.rowIndex=\"1\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.CreateCurr\"/>\n        <TextField fx:id=\"newCurrencyTextField\" minWidth=\"80\" GridPane.columnIndex=\"1\"/>\n        <Button fx:id=\"addButton\" text=\"%Button.Add\" onAction=\"#handleNewCurrencyAction\" GridPane.columnIndex=\"2\"/>\n    </GridPane>\n\n    <ButtonBar GridPane.rowIndex=\"2\">\n        <buttons>\n            <Button text=\"%Button.Close\" onAction=\"#handleCloseAction\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/currency/EditExchangeRates.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ProgressBar?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.CurrencyComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane prefHeight=\"400.0\" prefWidth=\"600.0\" xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\"\n          fx:controller=\"jgnash.uifx.dialog.currency.EditExchangeRatesController\" styleClass=\"dialog, form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <GridPane styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <Label text=\"%Word.Exchange\"/>\n        <CurrencyComboBox fx:id=\"baseCurrencyComboBox\" maxWidth=\"Infinity\" prefWidth=\"150.0\"\n                          GridPane.columnIndex=\"1\"/>\n        <Label GridPane.columnIndex=\"2\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"ARROW_RIGHT_BOLD\"/>\n            </graphic>\n        </Label>\n        <CurrencyComboBox fx:id=\"targetCurrencyComboBox\" maxWidth=\"Infinity\" prefWidth=\"150.0\"\n                          GridPane.columnIndex=\"3\"/>\n    </GridPane>\n    <Separator GridPane.rowIndex=\"1\"/>\n    <GridPane GridPane.rowIndex=\"2\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"ALWAYS\"/>\n        </rowConstraints>\n        <Label text=\"%Label.Date\"/>\n        <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"1\"/>\n        <Label text=\"%Label.ExchangeRate\" GridPane.columnIndex=\"2\"/>\n        <DecimalTextField fx:id=\"exchangeRateField\" GridPane.columnIndex=\"3\" maxWidth=\"Infinity\"/>\n        <ButtonBar GridPane.columnSpan=\"4\" GridPane.rowIndex=\"1\" GridPane.fillWidth=\"true\">\n            <buttons>\n                <Button fx:id=\"addButton\" onAction=\"#handleAddAction\" text=\"%Button.Add\"\n                        ButtonBar.buttonUniformSize=\"true\"/>\n                <Button fx:id=\"deleteButton\" onAction=\"#handleDeleteAction\" text=\"%Button.Delete\"\n                        ButtonBar.buttonUniformSize=\"true\"/>\n                <Button fx:id=\"clearButton\" onAction=\"#handleClearAction\" text=\"%Button.Clear\"\n                        ButtonBar.buttonUniformSize=\"true\"/>\n            </buttons>\n        </ButtonBar>\n        <TableViewEx fx:id=\"exchangeRateTable\" minHeight=\"150.0\" minWidth=\"100.0\" prefHeight=\"150.0\"\n                     prefWidth=\"200.0\" GridPane.columnSpan=\"4\" GridPane.rowIndex=\"2\"/>\n    </GridPane>\n    <Separator GridPane.rowIndex=\"3\"/>\n    <GridPane GridPane.rowIndex=\"4\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <Button fx:id=\"updateOnlineButton\" onAction=\"#handleUpdateAction\" text=\"%Button.UpdateOnline\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n            </graphic>\n        </Button>\n        <ProgressBar fx:id=\"progressBar\" maxWidth=\"Infinity\" progress=\"0.0\" GridPane.columnIndex=\"1\"/>\n        <Button fx:id=\"stopButton\" onAction=\"#handleStopAction\" text=\"%Button.Stop\" GridPane.columnIndex=\"2\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"STOP_CIRCLE\"/>\n            </graphic>\n        </Button>\n    </GridPane>\n    <ButtonBar GridPane.rowIndex=\"5\">\n        <buttons>\n            <Button cancelButton=\"true\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/currency/ModifyCurrency.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n          fx:controller=\"jgnash.uifx.dialog.currency.ModifyCurrencyController\" styleClass=\"dialog, form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"10.0\" prefWidth=\"150.0\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"120.0\" prefWidth=\"120.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"SOMETIMES\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <ListView fx:id=\"listView\" prefHeight=\"200.0\" prefWidth=\"200.0\" GridPane.rowSpan=\"6\"/>\n\n    <Label text=\"%Label.Symbol\" GridPane.columnIndex=\"1\"/>\n    <TextField fx:id=\"symbolTextField\" GridPane.columnIndex=\"2\"/>\n\n    <Label text=\"%Label.Description\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n    <TextField fx:id=\"descriptionTextField\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n\n    <Label text=\"%Label.Scale\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n    <IntegerTextField fx:id=\"scaleTextField\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n\n    <Label text=\"%Label.Prefix\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\"/>\n    <TextField fx:id=\"prefixTextField\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"3\"/>\n\n    <Label text=\"%Label.Suffix\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"4\"/>\n    <TextField fx:id=\"suffixTextField\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"4\"/>\n\n    <ButtonBar GridPane.columnSpan=\"3\" GridPane.rowIndex=\"6\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleClearAction\" text=\"%Button.Clear\"\n                    ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"true\"/>\n            <Button fx:id=\"applyButton\" mnemonicParsing=\"false\" onAction=\"#handleApplyAction\" text=\"%Button.Apply\"\n                    ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"true\"/>\n            <Button ButtonBar.buttonData=\"SMALL_GAP\" visible=\"false\"/>\n        </buttons>\n    </ButtonBar>\n    <ButtonBar GridPane.columnSpan=\"3\" GridPane.rowIndex=\"7\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\" cancelButton=\"true\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/AccountTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.control.RadioButton?>\n<VBox fx:controller=\"jgnash.uifx.dialog.options.AccountTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog, form\">\n\n    <fx:define>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n        <ToggleGroup fx:id=\"importToggleGroup\"/>\n    </fx:define>\n\n    <TitledPane text=\"%Title.Display\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <Label text=\"%Label.AccountSeparator\"/>\n            <TextField fx:id=\"accountSeparatorTextField\" GridPane.columnIndex=\"1\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.Terms\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <CheckBox text=\"%Button.AccTerms\" fx:id=\"useAccountingTermsCheckBox\"/>\n        </GridPane>\n\n    </TitledPane>\n\n    <TitledPane text=\"%Title.ReverseAccountBalances\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"SOMETIMES\" />\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <RadioButton text=\"%Button.None\" fx:id=\"noAccountsRadioButton\" toggleGroup=\"$toggleGroup\"/>\n            <RadioButton text=\"%Button.CreditAccounts\" fx:id=\"creditAccountsRadioButton\" toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"1\"/>\n            <RadioButton text=\"%Button.IncomeAndExpense\" fx:id=\"incomeExpenseAccountsRadioButton\" toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"2\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.TransactionImport\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"SOMETIMES\" />\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <RadioButton text=\"%Button.MatchAccountOnly\" fx:id=\"accountOnlyRadioButton\" toggleGroup=\"$importToggleGroup\"/>\n            <RadioButton text=\"%Button.MatchAllTrans\" fx:id=\"allRadioButton\" toggleGroup=\"$importToggleGroup\" GridPane.rowIndex=\"1\"/>\n        </GridPane>\n\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/DataProviderTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Hyperlink?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.TextFieldEx?>\n\n<VBox fx:controller=\"jgnash.uifx.dialog.options.DataProviderTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.IEXCloud\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <Label text=\"%Label.IEXCloudSecretKey\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"/>\n            <TextFieldEx fx:id=\"iexPrivateKeyTextField\" onAction=\"#handleIexCloudKey\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"/>\n            <Hyperlink text = \"%Label.IEXCloudAttribution\"  onAction=\"#handleHyperLink\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"2\" GridPane.rowIndex=\"1\"/>\n        </GridPane>\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/FormatsTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n\n<VBox fx:controller=\"jgnash.uifx.dialog.options.FormatsTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.NumericFormats\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <Label text=\"%Label.FullNumFormat\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"/>\n            <ComboBox fx:id=\"fullNumberFormatComboBox\" editable=\"true\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"/>\n            <Label text=\"%Label.ShortNumFormat\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n            <ComboBox fx:id=\"shortNumberFormatComboBox\" editable=\"true\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.DateFormats\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <Label text=\"%Label.DateFormat\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"/>\n            <ComboBox fx:id=\"dateFormatComboBox\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"/>\n        </GridPane>\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/GeneralTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.layout.VBox?>\n\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.ToggleGroup?>\n\n<VBox fx:controller=\"jgnash.uifx.dialog.options.GeneralTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog, form\">\n\n    <fx:define>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n    </fx:define>\n\n    <GridPane styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"ALWAYS\"/>\n        </rowConstraints>\n\n        <TitledPane text=\"%Title.Entry\" collapsible=\"false\">\n            <GridPane styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n                <CheckBox text=\"%Button.SelectText\" fx:id=\"selectOnFocusCheckBox\" />\n            </GridPane>\n        </TitledPane>\n\n        <TitledPane text=\"%Title.Display\" collapsible=\"false\" GridPane.rowIndex=\"1\">\n            <GridPane styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n                <CheckBox text=\"%Button.AnimationsEnabled\" fx:id=\"animationsEnabledCheckBox\" GridPane.rowIndex=\"0\"/>\n                <CheckBox text=\"%Button.AutoResizeColumns\" fx:id=\"autoPackRegisterTableCheckBox\" GridPane.rowIndex=\"1\"/>\n            </GridPane>\n        </TitledPane>\n\n        <TitledPane text=\"%Title.ButtonOrder\" GridPane.rowIndex=\"2\" collapsible=\"false\">\n            <GridPane styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n                <RadioButton fx:id=\"windowsStyleRadioButton\" text=\"%Button.Order.WindowsOS\"\n                             toggleGroup=\"$toggleGroup\"/>\n                <RadioButton fx:id=\"macOSStyleRadioButton\" text=\"%Button.Order.MacOS\"\n                             toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"1\"/>\n                <RadioButton fx:id=\"linuxStyleRadioButton\" text=\"%Button.Order.LinuxOS\"\n                             toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"2\"/>\n            </GridPane>\n        </TitledPane>\n\n        <TitledPane text=\"%Title.Filters\" GridPane.rowIndex=\"3\" collapsible=\"false\">\n            <GridPane styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n                <CheckBox text=\"%Button.UseRegexForFilter\" fx:id=\"filterRegexEnabledCheckBox\" />\n            </GridPane>\n        </TitledPane>\n    </GridPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/NetworkTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import javafx.scene.control.PasswordField?>\n<VBox fx:controller=\"jgnash.uifx.dialog.options.NetworkTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.HTTPProxy\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"10\" minWidth=\"10\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <CheckBox text=\"%Button.UseProxy\"  fx:id=\"useProxyCheckBox\" GridPane.columnSpan=\"3\"/>\n            <Label text=\"%Label.Host\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n            <TextField fx:id=\"hostTextField\" GridPane.rowIndex=\"1\"  GridPane.columnIndex=\"2\"/>\n            <Label text=\"%Label.Port\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n            <IntegerTextField fx:id=\"portTextField\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"2\"/>\n\n            <CheckBox text=\"%Button.HTTPAuth\"  fx:id=\"requireAuthCheckBox\" GridPane.columnSpan=\"3\" GridPane.rowIndex=\"3\"/>\n            <Label text=\"%Label.UserName\" GridPane.rowIndex=\"4\" GridPane.columnIndex=\"1\"/>\n            <TextField fx:id=\"userNameTextField\" GridPane.rowIndex=\"4\" GridPane.columnIndex=\"2\"/>\n            <Label text=\"%Label.Password\" GridPane.rowIndex=\"5\" GridPane.columnIndex=\"1\"/>\n            <PasswordField fx:id=\"passwordField\" GridPane.rowIndex=\"5\" GridPane.columnIndex=\"2\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.Connection\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <Label text=\"%Label.ConnTimeout\"/>\n            <Spinner fx:id=\"timeoutSpinner\" prefWidth=\"80\" GridPane.columnIndex=\"1\"/>\n            <Label text=\"%Word.Seconds\" GridPane.columnIndex=\"2\"/>\n        </GridPane>\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/OptionDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Tab?>\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.ColumnConstraints?>\n\n<GridPane fx:controller=\"jgnash.uifx.dialog.options.OptionDialogController\" minHeight=\"-Infinity\"\n          xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!--suppress JavaFxUnresolvedStyleClassReference -->\n    <TabPane fx:id=\"tabPane\" tabClosingPolicy=\"UNAVAILABLE\" styleClass=\"floating\">\n        <Tab text=\"%Tab.StartupShutdown\">\n            <fx:include source=\"StartupShutdownTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.General\">\n            <fx:include source=\"GeneralTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.Formats\">\n            <fx:include source=\"FormatsTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.Accounts\">\n            <fx:include source=\"AccountTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.Register\">\n            <fx:include source=\"RegisterTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.Reminders\">\n            <fx:include source=\"RemindersTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.Report\">\n            <fx:include source=\"ReportTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.Network\">\n            <fx:include source=\"NetworkTab.fxml\"/>\n        </Tab>\n        <Tab text=\"%Tab.DataProviders\">\n            <fx:include source=\"DataProviderTab.fxml\"/>\n        </Tab>\n    </TabPane>\n\n    <ButtonBar GridPane.rowIndex=\"1\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/RegisterTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n\n<?import javafx.scene.control.Tooltip?>\n<VBox fx:controller=\"jgnash.uifx.dialog.options.RegisterTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog, form\">\n\n    <fx:define>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n    </fx:define>\n\n    <TitledPane text=\"%Title.DefaultBehavior\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <CheckBox text=\"%Button.RegDate\" fx:id=\"rememberLastTranDateCheckBox\"/>\n            <CheckBox text=\"%Button.ConfirmTransDelete\" fx:id=\"confirmDeleteCheckBox\" GridPane.rowIndex=\"1\"/>\n            <CheckBox text=\"%Button.RestoreLastTranTab\" fx:id=\"restoreLastRegisterTab\" GridPane.rowIndex=\"2\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.AutoComplete\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"10\" minWidth=\"10\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <CheckBox text=\"%Button.EnableAutoComplete\" fx:id=\"enableAutoCompleteCheckBox\" GridPane.columnSpan=\"2\"/>\n            <CheckBox text=\"%Button.MatchCaseSensitive\" fx:id=\"caseSensitiveCheckBox\" GridPane.rowIndex=\"1\"\n                      GridPane.columnIndex=\"1\"/>\n            <CheckBox text=\"%Button.UseFuzzyMatch\" fx:id=\"fuzzyMatchCheckBox\" GridPane.rowIndex=\"2\"\n                      GridPane.columnIndex=\"1\">\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.FuzzyMatch\"/>\n                </tooltip>\n            </CheckBox>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.ReconcileSettings\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <RadioButton text=\"%Button.ReconcileDisable\" fx:id=\"disableReconcileCheckBox\" toggleGroup=\"$toggleGroup\"/>\n            <RadioButton text=\"%Button.ReconcileBoth\" fx:id=\"reconcileBothCheckBox\" toggleGroup=\"$toggleGroup\"\n                         GridPane.rowIndex=\"1\"/>\n            <RadioButton text=\"%Button.ReconcileIncomeExpense\" fx:id=\"reconcileIncomeExpenseCheckBox\"\n                         toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"2\"/>\n        </GridPane>\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/RemindersTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.layout.VBox?>\n\n<VBox fx:controller=\"jgnash.uifx.dialog.options.RemindersTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog, form\">\n\n    <CheckBox text=\"%Button.ConfirmReminderDelete\" fx:id=\"confirmOnDeleteCheckBox\"/>\n\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/ReportTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n\n<VBox fx:controller=\"jgnash.uifx.dialog.options.ReportTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.DefaultBehavior\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <CheckBox text=\"%Button.ReportDate\" fx:id=\"rememberLastReportDateCheckBox\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.Fonts\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.Monospace\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"0\"/>\n            <ComboBox fx:id=\"monoFontComboBox\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.Proportional\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"0\"/>\n            <ComboBox fx:id=\"proportionalFontComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.HeaderTitle\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"0\"/>\n            <ComboBox fx:id=\"headerFontComboBox\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n\n        </GridPane>\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/StartupShutdownTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n\n<VBox fx:controller=\"jgnash.uifx.dialog.options.StartupShutdownTabController\" minHeight=\"-Infinity\"\n      xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.Startup\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"SOMETIMES\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <CheckBox text=\"%Button.OpenLastOnStartup\" fx:id=\"openLastCheckBox\"/>\n            <CheckBox text=\"%Button.CheckForUpdates\" fx:id=\"checkForUpdatesCheckBox\" GridPane.rowIndex=\"1\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.Shutdown\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <CheckBox text=\"%Button.CreateTimeFile\" fx:id=\"createBackupsCheckBox\"/>\n            <CheckBox text=\"%Button.RemoveOldBackups\" fx:id=\"removeOldBackupsCheckBox\" GridPane.rowIndex=\"1\"/>\n            <Label text=\"%Label.MaxBackupCount\" GridPane.rowIndex=\"2\"/>\n            <Spinner fx:id=\"backupCountSpinner\" prefWidth=\"80\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n        </GridPane>\n\n    </TitledPane>\n\n    <TitledPane text=\"%Title.BackgroundUpdate\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"SOMETIMES\" />\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <CheckBox text=\"%Button.UpdateCurrenciesStartup\" fx:id=\"updateCurrencies\"/>\n            <CheckBox text=\"%Button.UpdateSecuritiesStartup\" fx:id=\"updateSecurities\" GridPane.rowIndex=\"1\"/>\n        </GridPane>\n    </TitledPane>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/options/TransactionNumberDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane fx:controller=\"jgnash.uifx.dialog.options.TransactionNumberDialogController\" minHeight=\"-Infinity\"\n          xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <ListView fx:id=\"listView\" minHeight=\"90\" prefHeight=\"180\"/>\n\n    <VBox GridPane.columnIndex=\"1\" styleClass=\"form\">\n        <Button fx:id=\"upButton\" onAction=\"#handleUpAction\" contentDisplay=\"GRAPHIC_ONLY\" minWidth=\"50\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LEVEL_UP\"/>\n            </graphic>\n        </Button>\n        <Button fx:id=\"downButton\" onAction=\"#handleDownAction\" contentDisplay=\"GRAPHIC_ONLY\" minWidth=\"50\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LEVEL_DOWN\"/>\n            </graphic>\n        </Button>\n    </VBox>\n\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"1\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button onAction=\"#handleOkayAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button onAction=\"#handleCloseAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/security/CreateModifySecurities.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.CurrencyComboBox?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import jgnash.uifx.control.QuoteSourceComboBox?>\n<GridPane prefHeight=\"380.0\" prefWidth=\"550.0\" xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n          fx:controller=\"jgnash.uifx.dialog.security.CreateModifySecuritiesController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"150.0\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"180.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <ListView fx:id=\"listView\" prefHeight=\"180.0\" prefWidth=\"150.0\"/>\n\n    <GridPane GridPane.columnIndex=\"1\" GridPane.vgrow=\"NEVER\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"150.0\" prefWidth=\"150.0\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <Label text=\"%Label.Symbol\"/>\n        <TextField fx:id=\"symbolTextField\" GridPane.columnIndex=\"1\"/>\n\n        <Label text=\"%Label.ISIN\" GridPane.rowIndex=\"1\"/>\n        <TextField fx:id=\"cusipTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\">\n            <tooltip>\n                <Tooltip text=\"%ToolTip.ISIN\"/>\n            </tooltip>\n        </TextField>\n\n        <Label text=\"%Label.QuoteSource\" GridPane.rowIndex=\"2\"/>\n        <QuoteSourceComboBox fx:id=\"quoteSourceComboBox\" prefWidth=\"180.0\" GridPane.columnIndex=\"1\"\n                             GridPane.rowIndex=\"2\"/>\n\n        <Label text=\"%Label.Description\" GridPane.rowIndex=\"3\"/>\n        <TextField fx:id=\"descriptionTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\"/>\n\n        <Label text=\"%Label.Scale\" GridPane.rowIndex=\"4\"/>\n        <IntegerTextField fx:id=\"scaleTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"4\">\n            <tooltip>\n                <Tooltip text=\"%ToolTip.Scale\"/>\n            </tooltip>\n        </IntegerTextField>\n\n        <Label text=\"%Label.ReportedCurrency\" GridPane.rowIndex=\"5\"/>\n        <CurrencyComboBox fx:id=\"reportedCurrencyComboBox\" prefWidth=\"180.0\" GridPane.columnIndex=\"1\"\n                          GridPane.rowIndex=\"5\"/>\n    </GridPane>\n\n    <ButtonBar GridPane.columnSpan=\"2\" GridPane.rowIndex=\"1\" GridPane.vgrow=\"NEVER\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleNewAction\" text=\"%Button.New\" ButtonBar.buttonData=\"LEFT\"/>\n            <Button fx:id=\"deleteButton\" mnemonicParsing=\"false\" onAction=\"#handleDeleteAction\" text=\"%Button.Delete\"\n                    ButtonBar.buttonData=\"LEFT\"/>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCancelAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"LEFT\"/>\n            <Button ButtonBar.buttonData=\"SMALL_GAP\" visible=\"false\"/>\n            <Button fx:id=\"applyButton\" mnemonicParsing=\"false\" onAction=\"#handleApplyAction\" text=\"%Button.Apply\" ButtonBar.buttonData=\"RIGHT\"/>\n        </buttons>\n    </ButtonBar>\n    <ButtonBar GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\" GridPane.vgrow=\"NEVER\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\" ButtonBar.buttonData=\"OK_DONE\"/>\n        </buttons>\n    </ButtonBar>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/security/HistoricalImport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ProgressBar?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.CheckListView?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane prefHeight=\"400.0\" prefWidth=\"600.0\" xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\"\n          fx:controller=\"jgnash.uifx.dialog.security.HistoricalImportController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.StartDate\"/>\n    <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"2\"/>\n    <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\" maxWidth=\"Infinity\"/>\n\n    <ButtonBar GridPane.columnSpan=\"4\" GridPane.rowIndex=\"1\">\n        <buttons>\n            <Button fx:id=\"selectAllButton\" onAction=\"#handleSelectAllAction\" text=\"%Button.SelectAll\"\n                    ButtonBar.buttonData=\"LEFT\"\n                    ButtonBar.buttonUniformSize=\"true\"/>\n            <Button fx:id=\"clearAllButton\" onAction=\"#handleClearAllAction\" text=\"%Button.ClearAll\"\n                    ButtonBar.buttonData=\"LEFT\"\n                    ButtonBar.buttonUniformSize=\"true\"/>\n            <Button fx:id=\"invertAllButton\" onAction=\"#handleInvertSelectionAction\" text=\"%Button.InvertSelection\"\n                    ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"true\"/>\n            <Button ButtonBar.buttonData=\"SMALL_GAP\" visible=\"false\"/>\n        </buttons>\n    </ButtonBar>\n    <CheckListView fx:id=\"checkListView\" prefHeight=\"200.0\" prefWidth=\"150\" GridPane.columnSpan=\"4\"\n                   GridPane.rowIndex=\"2\"/>\n    <ProgressBar fx:id=\"progressBar\" maxWidth=\"Infinity\" prefWidth=\"150.0\" progress=\"0.0\"\n                 GridPane.columnSpan=\"4\" GridPane.rowIndex=\"3\"/>\n    <GridPane GridPane.columnSpan=\"4\" GridPane.rowIndex=\"4\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <ButtonBar>\n            <buttons>\n                <Button fx:id=\"startButton\" onAction=\"#handleStartAction\" text=\"%Button.Start\"\n                        ButtonBar.buttonData=\"LEFT\">\n                    <graphic>\n                        <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n                    </graphic>\n                </Button>\n                <Button fx:id=\"stopButton\" onAction=\"#handleStopAction\" text=\"%Button.Stop\"\n                        ButtonBar.buttonData=\"RIGHT\">\n                    <graphic>\n                        <MaterialDesignLabel glyphName=\"STOP_CIRCLE\"/>\n                    </graphic>\n                </Button>\n            </buttons>\n        </ButtonBar>\n\n        <Label fx:id=\"messageLabel\" GridPane.columnIndex=\"1\"/>\n    </GridPane>\n    <ButtonBar GridPane.columnSpan=\"4\" GridPane.rowIndex=\"5\">\n        <buttons>\n            <Button cancelButton=\"true\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/dialog/security/SecurityHistory.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.SecurityHistoryEventTypeComboBox?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane prefHeight=\"400.0\" prefWidth=\"800.0\" xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\"\n          fx:controller=\"jgnash.uifx.dialog.security.SecurityHistoryController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"SOMETIMES\"/>\n        <RowConstraints vgrow=\"SOMETIMES\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <GridPane styleClass=\"form\" GridPane.columnSpan=\"2\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.Security\"/>\n        <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.columnIndex=\"1\"\n                          GridPane.hgrow=\"ALWAYS\"/>\n        <Separator GridPane.columnSpan=\"2\" GridPane.rowIndex=\"1\"/>\n    </GridPane>\n\n    <StackPane fx:id=\"chartPane\" GridPane.rowIndex=\"1\" GridPane.columnSpan=\"2\" minHeight=\"140\"/>\n\n    <TitledPane text=\"%Title.PriceHistory\" collapsible=\"false\" GridPane.rowIndex=\"2\" >\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95.0\" prefWidth=\"100.0\"/>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95.0\" prefWidth=\"100.0\"/>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95.0\" prefWidth=\"100.0\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"SOMETIMES\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <TableViewEx fx:id=\"priceTableView\" minHeight=\"150.0\" prefWidth=\"180.0\" GridPane.rowIndex=\"0\"\n                         GridPane.columnSpan=\"6\"/>\n\n            <Label text=\"%Label.Date\" GridPane.rowIndex=\"1\"/>\n            <DatePickerEx fx:id=\"historyDatePicker\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.Close\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"2\"/>\n            <DecimalTextField fx:id=\"closeTextField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"3\"/>\n\n            <Label text=\"%Label.Volume\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"4\"/>\n            <IntegerTextField fx:id=\"volumeTextField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"5\"/>\n\n            <Label text=\"%Label.High\" GridPane.rowIndex=\"2\"/>\n            <DecimalTextField fx:id=\"highTextField\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.Low\"  GridPane.rowIndex=\"2\" GridPane.columnIndex=\"2\"/>\n            <DecimalTextField fx:id=\"lowTextField\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"3\"/>\n\n            <ButtonBar GridPane.rowIndex=\"3\" GridPane.columnSpan=\"6\">\n                <buttons>\n                    <Button fx:id=\"deletePriceButton\" onAction=\"#handleDeletePriceAction\" text=\"%Button.Delete\"\n                            ButtonBar.buttonData=\"LEFT\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                        </graphic>\n                    </Button>\n                    <Button onAction=\"#handleClearPriceAction\" text=\"%Button.Clear\" ButtonBar.buttonData=\"LEFT\"/>\n                    <Button fx:id=\"addPriceButton\" onAction=\"#handleAddPriceAction\" text=\"%Button.Add\" ButtonBar.buttonData=\"LEFT\"/>\n                    <Button fx:id=\"updatePriceButton\" onAction=\"#handleOnlineUpdate\" text=\"%Button.UpdateOnline\"\n                            ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"false\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n                        </graphic>\n                    </Button>\n                </buttons>\n            </ButtonBar>\n\n            <Separator GridPane.rowIndex=\"4\" GridPane.columnSpan=\"6\"/>\n\n            <ButtonBar GridPane.rowIndex=\"5\" GridPane.columnSpan=\"6\">\n                <buttons>\n                    <Button text=\"%Button.DeleteWeekends\" onAction=\"#handleRemoveWeekendsAction\" ButtonBar.buttonData=\"LEFT\"\n                            ButtonBar.buttonUniformSize=\"false\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                        </graphic>\n                        <tooltip>\n                            <Tooltip text=\"%ToolTip.DeleteWeekendSecurityHistory\"/>\n                        </tooltip>\n                    </Button>\n                    <Button text=\"%Button.KeepMondays\" onAction=\"#handleKeepMondaysOnlyAction\" ButtonBar.buttonData=\"LEFT\"\n                            ButtonBar.buttonUniformSize=\"false\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                        </graphic>\n                        <tooltip>\n                            <Tooltip text=\"%ToolTip.DeleteAllExceptMondaySecurityHistory\"/>\n                        </tooltip>\n                    </Button>\n                    <Button text=\"%Button.KeepFridays\" onAction=\"#handleKeepFridaysOnlyAction\" ButtonBar.buttonData=\"LEFT\"\n                            ButtonBar.buttonUniformSize=\"false\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                        </graphic>\n                        <tooltip>\n                            <Tooltip text=\"%ToolTip.DeleteAllExceptFridaySecurityHistory\"/>\n                        </tooltip>\n                    </Button>\n                </buttons>\n            </ButtonBar>\n\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane text=\"%Title.EventHistory\" collapsible=\"false\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\">\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95.0\" prefWidth=\"100.0\"/>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"105.0\" prefWidth=\"105.0\"/>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n                <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95.0\" prefWidth=\"100.0\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"SOMETIMES\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <TableViewEx fx:id=\"eventTableView\" minHeight=\"150.0\" prefWidth=\"180.0\" GridPane.rowIndex=\"0\"\n                       GridPane.columnSpan=\"6\"/>\n\n            <Label text=\"%Label.Date\" GridPane.rowIndex=\"1\"/>\n            <DatePickerEx fx:id=\"eventDatePicker\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.Event\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"2\"/>\n            <SecurityHistoryEventTypeComboBox fx:id=\"securityEventTypeComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"3\"/>\n\n            <Label text=\"%Label.Value\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"4\"/>\n            <DecimalTextField fx:id=\"eventValueTextField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"5\"/>\n\n            <ButtonBar GridPane.rowIndex=\"2\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"6\">\n                <buttons>\n                    <Button fx:id=\"deleteEventButton\" onAction=\"#handleDeleteEventAction\" text=\"%Button.Delete\"\n                            ButtonBar.buttonData=\"LEFT\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                        </graphic>\n                    </Button>\n                    <Button onAction=\"#handleClearEventAction\" text=\"%Button.Clear\" ButtonBar.buttonData=\"LEFT\"/>\n                    <Button fx:id=\"addEventButton\" onAction=\"#handleAddEventAction\" text=\"%Button.Add\" ButtonBar.buttonData=\"LEFT\"/>\n                    <Button fx:id=\"updateEventButton\" onAction=\"#handleOnlineEventUpdate\" text=\"%Button.UpdateOnline\"\n                            ButtonBar.buttonData=\"LEFT\" ButtonBar.buttonUniformSize=\"false\">\n                        <graphic>\n                            <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n                        </graphic>\n                    </Button>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </TitledPane>\n\n    <ButtonBar GridPane.rowIndex=\"3\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button onAction=\"#handleCloseAction\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/AccountBalanceChart.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.chart.BarChart?>\n<?import javafx.scene.chart.CategoryAxis?>\n<?import javafx.scene.chart.NumberAxis?>\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.report.AccountBalanceChartController\" minWidth=\"850\" minHeight=\"600\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\">\n    <fx:define>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n    </fx:define>\n    <top>\n        <ToolBar>\n            <Button text=\"%Button.SaveImage\" onAction=\"#handleSaveAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE_IMAGE_O\"/>\n                </graphic>\n            </Button>\n            <Button text=\"%Button.CopyToClip\" onAction=\"#handleCopyToClipboard\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CLIPBOARD\"/>\n                </graphic>\n            </Button>\n            <Button text=\"%Button.Print\" onAction=\"#handlePrintAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"PRINT\"/>\n                </graphic>\n            </Button>\n        </ToolBar>\n    </top>\n    <center>\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.StartDate\"/>\n            <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"2\"/>\n            <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\"/>\n\n            <Label text=\"%Label.Period\" GridPane.columnIndex=\"4\"/>\n            <ComboBox fx:id=\"periodComboBox\" GridPane.columnIndex=\"5\"/>\n            <RadioButton fx:id=\"endingBalanceRadioButton\" text=\"%Button.EndingBalance\" toggleGroup=\"$toggleGroup\"\n                         GridPane.columnIndex=\"6\"/>\n            <RadioButton fx:id=\"runningBalanceRadioButton\" text=\"%Button.RunningBalance\" toggleGroup=\"$toggleGroup\"\n                         GridPane.columnIndex=\"7\" GridPane.halignment=\"LEFT\"/>\n\n            <CheckBox fx:id=\"invertBalanceCheckBox\" text=\"%Button.InvertBalances\" GridPane.columnIndex=\"8\">\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ReversedCredit\"/>\n                </tooltip>\n            </CheckBox>\n\n            <Label text=\"%Label.Account\" GridPane.rowIndex=\"1\" GridPane.valignment=\"BASELINE\"/>\n\n            <VBox fx:id=\"accountComboVBox\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" GridPane.columnSpan=\"7\"\n                  GridPane.valignment=\"BASELINE\" styleClass=\"form\">\n                <AccountComboBox fx:id=\"accountComboBox\" maxWidth=\"Infinity\"/>\n            </VBox>\n\n            <CheckBox text=\"%Button.IncludeSubAccounts\" fx:id=\"includeSubAccounts\" GridPane.valignment=\"BASELINE\"\n                      GridPane.rowIndex=\"1\" GridPane.columnIndex=\"8\"/>\n\n            <StackPane GridPane.rowIndex=\"2\" GridPane.columnSpan=\"9\" fx:id=\"chartPane\">\n                <BarChart fx:id=\"barChart\" title=\"%Title.AccountBalance\" legendSide=\"RIGHT\">\n                    <xAxis>\n                        <CategoryAxis side=\"BOTTOM\"/>\n                    </xAxis>\n                    <yAxis>\n                        <NumberAxis label=\"Currency\" side=\"LEFT\"/>\n                    </yAxis>\n                </BarChart>\n            </StackPane>\n\n            <ButtonBar GridPane.rowIndex=\"3\" GridPane.columnSpan=\"9\">\n                <buttons>\n                    <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                            ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/AccountRegisterReport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.report.AccountRegisterReportController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <GridPane styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"105\" maxWidth=\"105\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"105\" maxWidth=\"105\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\" prefWidth=\"180\" maxWidth=\"Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.StartDate\"/>\n        <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n        <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"2\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\"/>\n        <AccountComboBox fx:id=\"accountComboBox\" GridPane.columnIndex=\"4\"/>\n        <CheckBox text=\"%Button.DetailSplits\" fx:id=\"showSplitsCheckBox\" GridPane.columnIndex=\"5\"/>\n        <CheckBox text=\"%Button.ShowTimestamp\" fx:id=\"showTimestampCheckBox\" GridPane.columnIndex=\"6\"/>\n    </GridPane>\n\n    <GridPane styleClass=\"form\" GridPane.rowIndex=\"1\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" maxWidth=\"210\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" maxWidth=\"210\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <TextField fx:id=\"payeeFilterTextField\" promptText=\"%ToolTip.FilterPayee\" />\n        <TextField fx:id=\"memoFilterTextField\" promptText=\"%ToolTip.FilterMemo\" GridPane.columnIndex=\"1\"/>\n        <Button text=\"%Button.ResetAll\" onAction=\"#handleResetAll\" GridPane.columnIndex=\"3\"/>\n\n    </GridPane>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/BalanceByMonthOptionsDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.CheckBox?>\n<BorderPane fx:controller=\"jgnash.uifx.report.BalanceByMonthOptionsDialogController\"\n            xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"dialog\">\n    <fx:define>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n    </fx:define>\n\n    <center>\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.StartDate\"/>\n            <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.EndDate\" GridPane.rowIndex=\"1\"/>\n            <DatePickerEx fx:id=\"endDatePicker\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.Layout\" GridPane.rowIndex=\"2\"/>\n\n            <RadioButton fx:id=\"verticalRadioButton\" text=\"%Button.Vertical\" toggleGroup=\"$toggleGroup\"\n                         GridPane.rowIndex=\"2\"  GridPane.columnIndex=\"1\" selected=\"true\"/>\n\n            <RadioButton text=\"%Button.Horizontal\" toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"3\"\n                         GridPane.columnIndex=\"1\"/>\n\n            <CheckBox fx:id=\"defaultCurrencyCheckBox\" text=\"%Button.ForceDefaultCurrency\"\n                      GridPane.rowIndex=\"4\" GridPane.columnSpan=\"2\"/>\n\n            <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"5\" GridPane.columnSpan=\"2\">\n                <buttons>\n                    <Button onAction=\"#handleOkAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n                    <Button onAction=\"#handleCloseAction\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/BalanceSheetReport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.report.BalanceSheetReportController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.StartDate\"/>\n    <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"2\"/>\n    <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\"/>\n\n    <Label text=\"%Label.Resolution\" GridPane.columnIndex=\"4\"/>\n    <ComboBox fx:id=\"resolutionComboBox\" GridPane.columnIndex=\"5\"/>\n\n    <CheckBox fx:id=\"hideZeroBalanceAccounts\" text=\"%Button.HideZeroBalance\" GridPane.rowIndex=\"1\"\n              GridPane.columnIndex=\"0\" GridPane.columnSpan=\"5\"/>\n\n    <Button text=\"%Button.ResetAll\" onAction=\"#handleResetAll\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"5\"\n            GridPane.halignment=\"RIGHT\"/>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/IncomeExpenseBarChartDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.chart.BarChart?>\n<?import javafx.scene.chart.CategoryAxis?>\n<?import javafx.scene.chart.NumberAxis?>\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.report.IncomeExpenseBarChartDialogController\" minWidth=\"850\" minHeight=\"600\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\">\n    <top>\n      <ToolBar>\n          <Button text=\"%Button.SaveImage\" onAction=\"#handleSaveAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"FILE_IMAGE_O\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.CopyToClip\" onAction=\"#handleCopyToClipboard\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"CLIPBOARD\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.Print\" onAction=\"#handlePrintAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"PRINT\"/>\n              </graphic>\n          </Button>\n      </ToolBar>\n    </top>\n    <center>\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.StartDate\" />\n            <DatePickerEx fx:id=\"startDatePicker\"  GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.EndDate\"  GridPane.columnIndex=\"2\"/>\n            <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\"/>\n\n            <Label text=\"%Label.Period\"  GridPane.columnIndex=\"4\"/>\n            <ComboBox fx:id=\"periodComboBox\" GridPane.columnIndex=\"5\"/>\n\n            <StackPane GridPane.rowIndex=\"1\" GridPane.columnSpan=\"7\" fx:id=\"chartPane\">\n                <BarChart fx:id=\"barChart\" title=\"%Title.IncomeExpenseBarChart\" legendSide=\"RIGHT\">\n                    <xAxis>\n                        <CategoryAxis side=\"BOTTOM\" />\n                    </xAxis>\n                    <yAxis>\n                        <NumberAxis label=\"Currency\" side=\"LEFT\" />\n                    </yAxis>\n                </BarChart>\n            </StackPane>\n\n            <ButtonBar GridPane.rowIndex=\"2\" GridPane.columnSpan=\"7\">\n                <buttons>\n                    <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                            ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/IncomeExpensePayeePieChartDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DoughnutChart?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.report.IncomeExpensePayeePieChartDialogController\" minWidth=\"850\" minHeight=\"600\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\">\n    <top>\n      <ToolBar>\n          <Button text=\"%Button.SaveImage\" onAction=\"#handleSaveAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"FILE_IMAGE_O\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.CopyToClip\" onAction=\"#handleCopyToClipboard\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"CLIPBOARD\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.Print\" onAction=\"#handlePrintAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"PRINT\"/>\n              </graphic>\n          </Button>\n      </ToolBar>\n    </top>\n    <center>\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <GridPane maxWidth=\"Infinity\" styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                </columnConstraints>\n\n                <AccountComboBox fx:id=\"accountComboBox\" maxWidth=\"Infinity\"/>\n\n                <Label text=\"%Label.StartDate\" GridPane.columnIndex=\"1\"/>\n                <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"2\"/>\n\n                <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"3\"/>\n                <DatePickerEx fx:id=\"endDatePicker\"  GridPane.columnIndex=\"4\"/>\n            </GridPane>\n\n            <TitledPane text=\"%Title.Filters\" fx:id=\"titledPane\" expanded=\"false\" GridPane.rowIndex=\"1\" >\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILTER\"/>\n                </graphic>\n                <VBox styleClass=\"form\" fx:id=\"filtersPane\"/>\n            </TitledPane>\n\n            <GridPane GridPane.rowIndex=\"2\" fx:id=\"chartPane\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"ALWAYS\"/>\n                </rowConstraints>\n\n                <DoughnutChart fx:id=\"creditPieChart\"/>\n                <DoughnutChart fx:id=\"debitPieChart\" GridPane.columnIndex=\"1\"/>\n            </GridPane>\n\n            <ButtonBar GridPane.rowIndex=\"3\" >\n                <buttons>\n                    <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                            ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/IncomeExpensePieChartDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DoughnutChart?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.report.IncomeExpensePieChartDialogController\" minWidth=\"850\" minHeight=\"600\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\">\n    <top>\n      <ToolBar>\n          <Button text=\"%Button.SaveImage\" onAction=\"#handleSaveAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"FILE_IMAGE_O\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.CopyToClip\" onAction=\"#handleCopyToClipboard\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"CLIPBOARD\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.Print\" onAction=\"#handlePrintAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"PRINT\"/>\n              </graphic>\n          </Button>\n      </ToolBar>\n    </top>\n    <center>\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <GridPane maxWidth=\"Infinity\" styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                </columnConstraints>\n\n                <AccountComboBox fx:id=\"accountComboBox\" maxWidth=\"Infinity\"/>\n\n                <Label text=\"%Label.StartDate\" GridPane.columnIndex=\"1\"/>\n                <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"2\"/>\n\n                <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"3\"/>\n                <DatePickerEx fx:id=\"endDatePicker\"  GridPane.columnIndex=\"4\"/>\n            </GridPane>\n\n            <StackPane GridPane.rowIndex=\"1\" fx:id=\"chartPane\">\n                <DoughnutChart fx:id=\"pieChart\" />\n            </StackPane>\n\n            <ButtonBar GridPane.rowIndex=\"2\">\n                <buttons>\n                    <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                            ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/ListOfAccountsReport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.layout.GridPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.report.ListOfAccountsReportController\">\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/NetWorthReport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.report.NetWorthReportController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.StartDate\"/>\n    <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"2\"/>\n    <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\"/>\n\n    <Label text=\"%Label.Resolution\" GridPane.columnIndex=\"4\"/>\n    <ComboBox fx:id=\"resolutionComboBox\" GridPane.columnIndex=\"5\"/>\n\n    <CheckBox fx:id=\"hideZeroBalanceAccounts\" text=\"%Button.HideZeroBalance\" GridPane.rowIndex=\"1\"\n              GridPane.columnIndex=\"0\" GridPane.columnSpan=\"5\"/>\n\n    <Button text=\"%Button.ResetAll\" onAction=\"#handleResetAll\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"5\"\n            GridPane.halignment=\"RIGHT\"/>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/PortfolioReport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.CheckComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.report.PortfolioReportController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <GridPane styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"95\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"95\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.Account\"/>\n        <AccountComboBox fx:id=\"accountComboBox\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n        <Label text=\"%Label.StartDate\" GridPane.columnIndex=\"2\"/>\n        <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"3\"/>\n        <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"4\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n\n    <GridPane styleClass=\"form\" GridPane.rowIndex=\"1\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.ReportColumns\" GridPane.columnIndex=\"0\"/>\n        <CheckComboBox fx:id=\"columnComboBox\" GridPane.columnIndex=\"1\" maxWidth=\"280\"/>\n        <CheckBox fx:id=\"subAccountCheckBox\" text=\"%Button.IncludeSubAccounts\" GridPane.columnIndex=\"2\"/>\n        <CheckBox fx:id=\"longNameCheckBox\" text=\"%Button.UseLongNames\" GridPane.columnIndex=\"3\"/>\n\n        <Button text=\"%Button.ResetAll\" onAction=\"#handleResetAll\" GridPane.columnIndex=\"5\" GridPane.halignment=\"RIGHT\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/ProfitLossReport.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.report.ProfitLossReportController\" styleClass=\"form, dialog\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <GridPane styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.StartDate\"/>\n        <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\"/>\n        <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"2\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" GridPane.columnIndex=\"3\"/>\n        <Label text=\"%Label.Resolution\" GridPane.columnIndex=\"4\"/>\n        <ComboBox fx:id=\"resolutionComboBox\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n\n    <GridPane styleClass=\"form\" GridPane.rowIndex=\"1\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.SortOrder\"/>\n        <ComboBox fx:id=\"sortOrderComboBox\" onAction=\"#handleRefresh\" GridPane.columnIndex=\"1\"/>\n        <CheckBox fx:id=\"showLongNamesCheckBox\" text=\"%Button.UseLongNames\" GridPane.columnIndex=\"2\"/>\n        <CheckBox fx:id=\"hideZeroBalanceAccounts\" text=\"%Button.HideZeroBalance\" GridPane.columnIndex=\"3\"/>\n        <CheckBox fx:id=\"showAccountPercentages\" text=\"%Button.ShowPercentValues\" GridPane.columnIndex=\"4\"/>\n        <Button text=\"%Button.ResetAll\" onAction=\"#handleResetAll\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/TransactionTagPieChartDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DoughnutChart?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane fx:controller=\"jgnash.uifx.report.TransactionTagPieChartDialogController\" minWidth=\"800\" minHeight=\"600\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\">\n    <top>\n      <ToolBar>\n          <Button text=\"%Button.SaveImage\" onAction=\"#handleSaveAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"FILE_IMAGE_O\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.CopyToClip\" onAction=\"#handleCopyToClipboard\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"CLIPBOARD\"/>\n              </graphic>\n          </Button>\n          <Button text=\"%Button.Print\" onAction=\"#handlePrintAction\">\n              <graphic>\n                  <MaterialDesignLabel glyphName=\"PRINT\"/>\n              </graphic>\n          </Button>\n      </ToolBar>\n    </top>\n    <center>\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <GridPane maxWidth=\"Infinity\" styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"110\" minWidth=\"90\"/>\n                </columnConstraints>\n\n                <AccountComboBox fx:id=\"accountComboBox\" maxWidth=\"Infinity\"/>\n\n                <Label text=\"%Label.StartDate\" GridPane.columnIndex=\"1\"/>\n                <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"2\"/>\n\n                <Label text=\"%Label.EndDate\" GridPane.columnIndex=\"3\"/>\n                <DatePickerEx fx:id=\"endDatePicker\"  GridPane.columnIndex=\"4\"/>\n            </GridPane>\n\n            <StackPane GridPane.rowIndex=\"1\" fx:id=\"chartPane\">\n                <DoughnutChart fx:id=\"pieChart\" />\n            </StackPane>\n\n            <ButtonBar GridPane.rowIndex=\"2\">\n                <buttons>\n                    <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                            ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/pdf/PageFormatDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import javafx.scene.control.Separator?>\n\n<BorderPane prefWidth=\"450\" prefHeight=\"250\" fx:controller=\"jgnash.uifx.report.pdf.PageFormatDialogController\"\n            xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"dialog\">\n\n    <fx:define>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n    </fx:define>\n\n    <center>\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <TitledPane text=\"%Title.ReportSize\" collapsible=\"false\" GridPane.rowIndex=\"0\">\n                <GridPane styleClass=\"form\">\n                    <columnConstraints>\n                        <ColumnConstraints hgrow=\"NEVER\"/>\n                        <ColumnConstraints hgrow=\"ALWAYS\" prefWidth=\"70\" minWidth=\"50\"/>\n                        <ColumnConstraints hgrow=\"NEVER\"/>\n                        <ColumnConstraints hgrow=\"ALWAYS\" prefWidth=\"70\" minWidth=\"50\"/>\n                    </columnConstraints>\n                    <rowConstraints>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                    </rowConstraints>\n\n                    <Label text=\"%Label.Format\"/>\n                    <ComboBox fx:id=\"pageSizeComboBox\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"3\"\n                              maxWidth=\"Infinity\"/>\n\n                    <Label text=\"%Label.Units\" GridPane.rowIndex=\"1\"/>\n                    <ComboBox fx:id=\"unitsComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"\n                              GridPane.columnSpan=\"3\" maxWidth=\"Infinity\"/>\n\n                    <Separator GridPane.rowIndex=\"2\" GridPane.columnSpan=\"4\"/>\n\n                    <Label text=\"%Label.Width\" GridPane.rowIndex=\"3\" GridPane.columnIndex=\"0\"/>\n                    <DecimalTextField fx:id=\"widthField\" GridPane.rowIndex=\"3\" GridPane.columnIndex=\"1\"/>\n                    <Label text=\"%Label.Height\" GridPane.rowIndex=\"3\" GridPane.columnIndex=\"2\"/>\n                    <DecimalTextField fx:id=\"heightField\" GridPane.rowIndex=\"3\" GridPane.columnIndex=\"3\"/>\n                </GridPane>\n            </TitledPane>\n\n            <HBox GridPane.rowIndex=\"1\" styleClass=\"form\">\n                <TitledPane text=\"%Title.Orientation\" collapsible=\"false\" maxHeight=\"Infinity\">\n                    <GridPane styleClass=\"form\">\n                        <columnConstraints>\n                            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"120\"/>\n                        </columnConstraints>\n                        <rowConstraints>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                        </rowConstraints>\n                        <RadioButton fx:id=\"portraitRadioButton\" text=\"%Button.Portrait\" toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"0\"/>\n                        <RadioButton fx:id=\"landscapeRadioButton\" text=\"%Button.Landscape\" toggleGroup=\"$toggleGroup\" GridPane.rowIndex=\"1\"/>\n                    </GridPane>\n                </TitledPane>\n\n                <TitledPane text=\"%Title.Margins\" collapsible=\"false\" maxHeight=\"Infinity\" HBox.hgrow=\"ALWAYS\">\n                    <GridPane styleClass=\"form\">\n                        <columnConstraints>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"ALWAYS\" prefWidth=\"70\" minWidth=\"50\"/>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"ALWAYS\" prefWidth=\"70\" minWidth=\"50\"/>\n                        </columnConstraints>\n                        <rowConstraints>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                        </rowConstraints>\n                        <Label text=\"%Label.Left\" GridPane.columnIndex=\"0\"/>\n                        <DecimalTextField fx:id=\"leftMarginField\" GridPane.columnIndex=\"1\"/>\n                        <Label text=\"%Label.Right\" GridPane.columnIndex=\"2\"/>\n                        <DecimalTextField fx:id=\"rightMarginField\" GridPane.columnIndex=\"3\"/>\n                        <Label text=\"%Label.Top\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n                        <DecimalTextField fx:id=\"topMarginField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n                        <Label text=\"%Label.Bottom\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n                        <DecimalTextField fx:id=\"bottomMarginField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n                    </GridPane>\n                </TitledPane>\n            </HBox>\n\n            <ButtonBar GridPane.rowIndex=\"2\">\n                <buttons>\n                    <Button fx:id=\"okayButton\" onAction=\"#handleOkAction\" text=\"%Button.Ok\"\n                            ButtonBar.buttonData=\"OK_DONE\"/>\n                    <Button onAction=\"#handleCancelAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                </buttons>\n            </ButtonBar>\n        </GridPane>\n    </center>\n</BorderPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/report/pdf/ReportViewerDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ScrollPane?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.ToggleButton?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.effect.DropShadow?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<StackPane fx:id=\"stackPane\" fx:controller=\"jgnash.uifx.report.pdf.ReportViewerDialogController\"\n           xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\">\n    <fx:define>\n        <DropShadow fx:id=\"dropShadow\" color=\"grey\" offsetX=\"2\" offsetY=\"2\" blurType=\"ONE_PASS_BOX\"/>\n        <ToggleGroup fx:id=\"toggleGroup\"/>\n    </fx:define>\n    <BorderPane prefWidth=\"850\" prefHeight=\"600\">\n        <top>\n            <BorderPane>\n                <top>\n                    <ToolBar>\n                        <Button fx:id=\"saveButton\" onAction=\"#handleSaveAction\" text=\"%Button.Save\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"SAVE\"/>\n                            </graphic>\n                        </Button>\n                        <Separator/>\n                        <Button fx:id=\"reportFormatButton\" onAction=\"#handleFormatAction\" text=\"%Button.PageSetup\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"PRINT\"/>\n                            </graphic>\n                        </Button>\n                        <Spinner fx:id=\"fontSizeSpinner\" prefWidth=\"80\"/>\n                        <Separator/>\n                        <Button fx:id=\"firstButton\" onAction=\"#handleFirstAction\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"SKIP_BACKWARD\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"previousButton\" onAction=\"#handlePreviousAction\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"STEP_BACKWARD\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"nextButton\" onAction=\"#handleNextAction\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"STEP_FORWARD\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"lastButton\" onAction=\"#handleLastAction\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"SKIP_FORWARD\"/>\n                            </graphic>\n                        </Button>\n                        <Separator/>\n                        <Button fx:id=\"zoomOutButton\" onAction=\"#handleZoomOutAction\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"MINUS_CIRCLE\"/>\n                            </graphic>\n                        </Button>\n                        <ComboBox fx:id=\"zoomComboBox\" editable=\"true\" focusTraversable=\"false\"\n                                  onAction=\"#handleZoomChangedAction\"\n                                  prefWidth=\"80\"/>\n                        <Button fx:id=\"zoomInButton\" onAction=\"#handleZoomInAction\"\n                                disable=\"true\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"PLUS_CIRCLE\"/>\n                            </graphic>\n                        </Button>\n                        <Separator/>\n\n                        <ToggleButton fx:id=\"fitPageButton\" onAction=\"#handleActualSizeAction\" toggleGroup=\"$toggleGroup\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"ARROWS\"/>\n                            </graphic>\n                        </ToggleButton>\n                        <ToggleButton fx:id=\"fitHeightButton\" onAction=\"#handleFitHeightAction\" toggleGroup=\"$toggleGroup\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"ARROWS_V\"/>\n                            </graphic>\n                        </ToggleButton>\n                        <ToggleButton fx:id=\"fitWidthButton\" onAction=\"#handleFitPageWidthAction\" toggleGroup=\"$toggleGroup\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"ARROWS_H\"/>\n                            </graphic>\n                        </ToggleButton>\n\n                    </ToolBar>\n                </top>\n                <center>\n                    <StackPane fx:id=\"reportControllerPane\"/>\n                </center>\n            </BorderPane>\n        </top>\n\n        <center>\n            <ScrollPane fx:id=\"scrollPane\" BorderPane.alignment=\"CENTER\"\n                        vbarPolicy=\"ALWAYS\" styleClass=\"dialog\">\n                <VBox fx:id=\"pagePane\"/>\n            </ScrollPane>\n        </center>\n        <bottom>\n            <BorderPane styleClass=\"dialog\">\n                <center>\n                    <Label fx:id=\"statusLabel\"/>\n                </center>\n            </BorderPane>\n        </bottom>\n    </BorderPane>\n</StackPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/skin/BaseColorDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ColorPicker?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.control.Label?>\n<BorderPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n            fx:controller=\"jgnash.uifx.skin.BaseColorDialogController\" prefWidth=\"450.0\"\n            styleClass=\"form, dialog\">\n   <center>\n       <GridPane styleClass=\"form\">\n           <columnConstraints>\n               <ColumnConstraints hgrow=\"NEVER\"/>\n               <ColumnConstraints hgrow=\"ALWAYS\"/>\n               <ColumnConstraints hgrow=\"NEVER\"/>\n           </columnConstraints>\n           <rowConstraints>\n               <RowConstraints vgrow=\"NEVER\"/>\n               <RowConstraints vgrow=\"NEVER\"/>\n               <RowConstraints vgrow=\"NEVER\"/>\n           </rowConstraints>\n\n           <Label text=\"%Label.BaseColor\"/>\n           <ColorPicker fx:id=\"colorPicker\" maxWidth=\"Infinity\" GridPane.columnIndex=\"1\"/>\n           <Button text=\"%Button.RestoreDefault\" onAction=\"#handleDefaultColorAction\" GridPane.columnIndex=\"2\"/>\n\n           <Label text=\"%Label.AccentColor\" GridPane.rowIndex=\"1\"/>\n           <ColorPicker fx:id=\"accentColorPicker\" maxWidth=\"Infinity\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n           <Button text=\"%Button.RestoreDefault\" onAction=\"#handleDefaultAccentColorAction\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n\n           <Label text=\"%Label.FocusColor\" GridPane.rowIndex=\"2\"/>\n           <ColorPicker fx:id=\"focusColorPicker\" maxWidth=\"Infinity\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n           <Button text=\"%Button.RestoreDefault\" onAction=\"#handleDefaultFocusColorAction\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n       </GridPane>\n   </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/skin/FontSizeDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Slider?>\n<?import javafx.scene.layout.BorderPane?>\n\n<BorderPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n            fx:controller=\"jgnash.uifx.skin.FontSizeDialogController\" prefHeight=\"100.0\" prefWidth=\"350.0\"\n            styleClass=\"form, dialog\">\n   <center>\n      <Slider fx:id=\"slider\" max=\"150.0\" min=\"75.0\" showTickLabels=\"true\" showTickMarks=\"true\" value=\"100.0\"\n              minorTickCount=\"9\" blockIncrement=\"2.5\" BorderPane.alignment=\"CENTER\" />\n   </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/accounts/AccountProperties.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.CurrencyComboBox?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import jgnash.uifx.control.TextFieldEx?>\n<VBox xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n      fx:controller=\"jgnash.uifx.views.accounts.AccountPropertiesController\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.AccountInfo\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.Name\"/>\n            <Label text=\"%Label.Description\" GridPane.rowIndex=\"1\"/>\n            <Label text=\"%Label.AccountCode\" GridPane.rowIndex=\"2\"/>\n            <Label text=\"%Label.AccountNumber\" GridPane.rowIndex=\"3\"/>\n            <Label text=\"%Label.BankID\" GridPane.rowIndex=\"4\"/>\n            <Label text=\"%Label.Currency\" GridPane.rowIndex=\"5\"/>\n            <Label text=\"%Label.Securities\" GridPane.rowIndex=\"6\"/>\n            <Label text=\"%Label.AccountType\" GridPane.rowIndex=\"7\"/>\n            <Label text=\"%Label.AccountOptions\" GridPane.rowIndex=\"8\"/>\n            <TextFieldEx fx:id=\"nameTextField\" GridPane.columnIndex=\"1\"/>\n            <TextFieldEx fx:id=\"descriptionTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n            <IntegerTextField fx:id=\"accountCodeField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n            <TextFieldEx fx:id=\"accountNumberField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\"/>\n            <TextFieldEx fx:id=\"bankIdField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"4\"/>\n            <CurrencyComboBox fx:id=\"currencyComboBox\" minWidth=\"300.0\" GridPane.columnIndex=\"1\"\n                              GridPane.hgrow=\"ALWAYS\" GridPane.rowIndex=\"5\"/>\n            <Button fx:id=\"securitiesButton\" onAction=\"#handleSecuritiesButtonAction\" minWidth=\"200.0\" maxWidth=\"300\"\n                    mnemonicParsing=\"false\" text=\"%Word.None\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"6\"/>\n            <ComboBox fx:id=\"accountTypeComboBox\" minWidth=\"300.0\" prefWidth=\"150.0\"\n                      GridPane.columnIndex=\"1\" GridPane.rowIndex=\"7\"/>\n            <GridPane GridPane.columnIndex=\"1\" GridPane.rowIndex=\"8\" styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n                    <ColumnConstraints hgrow=\"SOMETIMES\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n\n                <CheckBox fx:id=\"lockedCheckBox\" mnemonicParsing=\"false\" text=\"%Button.Locked\"/>\n                <CheckBox fx:id=\"hideAccountCheckBox\" mnemonicParsing=\"false\" text=\"%Button.HideAccount\"\n                          GridPane.columnIndex=\"1\"/>\n                <CheckBox fx:id=\"placeholderCheckBox\" mnemonicParsing=\"false\" text=\"%Button.PlaceHolder\"\n                          GridPane.rowIndex=\"1\"/>\n                <CheckBox fx:id=\"excludeBudgetCheckBox\" mnemonicParsing=\"false\"\n                          text=\"%Button.ExcludeFromBudget\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n\n            </GridPane>\n            <Label text=\"%Title.ParentAccount\" GridPane.rowIndex=\"9\"/>\n            <Button fx:id=\"parentAccountButton\" mnemonicParsing=\"false\" text=\"Root\" GridPane.columnIndex=\"1\"\n                    GridPane.rowIndex=\"9\" minWidth=\"200\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" onAction=\"#handleParentAccountAction\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane animated=\"false\" text=\"%Title.Notes\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <BorderPane>\n            <center>\n                <TextArea fx:id=\"notesTextArea\" minWidth=\"280.0\" prefHeight=\"100.0\" prefWidth=\"280.0\"\n                          BorderPane.alignment=\"CENTER\"/>\n            </center>\n        </BorderPane>\n    </TitledPane>\n\n    <ButtonBar fx:id=\"buttonBar\" VBox.vgrow=\"NEVER\">\n        <buttons>\n            <Button text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#okAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#cancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/accounts/AccountTypeFilterForm.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n          fx:controller=\"jgnash.uifx.views.accounts.AccountTypeFilterFormController\" styleClass=\"dialog, form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"10.0\" prefWidth=\"50.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Title.VisibleAccountTypes\"/>\n    <Separator GridPane.columnIndex=\"1\"/>\n    <CheckBox fx:id=\"bankAccountCheckBox\" mnemonicParsing=\"false\" text=\"%Button.BankAccounts\"\n              GridPane.columnSpan=\"2\" GridPane.rowIndex=\"1\">\n    </CheckBox>\n    <CheckBox fx:id=\"expenseAccountCheckBox\" mnemonicParsing=\"false\" text=\"%Button.ExpenseAccounts\"\n              GridPane.columnSpan=\"2\" GridPane.rowIndex=\"2\">\n    </CheckBox>\n    <CheckBox fx:id=\"incomeAccountCheckBox\" mnemonicParsing=\"false\" text=\"%Button.IncomeAccounts\"\n              GridPane.columnSpan=\"2\" GridPane.rowIndex=\"3\">\n    </CheckBox>\n    <CheckBox fx:id=\"hiddenAccountCheckBox\" mnemonicParsing=\"false\" text=\"%Button.Hidden\"\n              GridPane.columnSpan=\"2\" GridPane.rowIndex=\"4\">\n    </CheckBox>\n    <ButtonBar GridPane.columnSpan=\"2\" GridPane.halignment=\"RIGHT\" GridPane.rowIndex=\"5\">\n        <buttons>\n            <Button text=\"%Button.Close\" onAction=\"#closeAction\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/accounts/AccountsView.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.control.TreeTableView?>\n<?import javafx.scene.layout.BorderPane?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.accounts.AccountsViewController\">\n    <top>\n        <ToolBar>\n            <Button fx:id=\"newButton\" text=\"%Button.New\" onAction=\"#handleNewAccountAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"PLUS\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.NewAccount\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"modifyButton\" text=\"%Button.Modify\" onAction=\"#handleModifyAccountAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"EDIT\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ModifyAccount\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"deleteButton\" text=\"%Button.Delete\" onAction=\"#handleDeleteAccountAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.DeleteAccount\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"reconcileButton\" text=\"%Button.Reconcile\" onAction=\"#handleReconcileAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"HANDSHAKE\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ReconcileAccount\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"filterButton\" text=\"%Button.Filter\" onAction=\"#handleFilterAccountAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILTER\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.FilterAccount\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"zoomButton\" text=\"%Button.Zoom\" onAction=\"#handleZoomAccountAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE_DOCUMENT_BOX_PLUS\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ZoomRegister\"/>\n                </tooltip>\n            </Button>\n            <Button text=\"%Button.Export\" onAction=\"#handleExport\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE_EXCEL_O\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ExportAccountTree\"/>\n                </tooltip>\n            </Button>\n        </ToolBar>\n    </top>\n    <center>\n        <TreeTableView fx:id=\"treeTableView\" prefHeight=\"200.0\" prefWidth=\"200.0\" tableMenuButtonVisible=\"true\"\n                       BorderPane.alignment=\"CENTER\">\n        </TreeTableView>\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/accounts/SelectAccountForm.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.TreeView?>\n<?import javafx.scene.layout.VBox?>\n\n<VBox xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n      fx:controller=\"jgnash.uifx.views.accounts.SelectAccountController\" styleClass=\"dialog, form\"\n        minWidth=\"350\">\n    <TreeView fx:id=\"treeView\" VBox.vgrow=\"ALWAYS\"/>\n    <ButtonBar fx:id=\"buttonBar\" VBox.vgrow=\"NEVER\"/>\n</VBox>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/budget/BudgetGoalsDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.TableViewEx?>\n\n<GridPane fx:controller=\"jgnash.uifx.views.budget.BudgetGoalsDialogController\" minHeight=\"-Infinity\"\n          xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.Period\" labelFor=\"$periodComboBox\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"0\"/>\n    <ComboBox fx:id=\"periodComboBox\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.Currency\" labelFor=\"$currencyLabel\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"0\"/>\n    <Label fx:id=\"currencyLabel\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n    <TableViewEx fx:id=\"goalTable\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"2\"\n                 prefHeight=\"200\" minHeight=\"240\"/>\n\n    <TitledPane text=\"%Title.SmartFill\" collapsible=\"false\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"2\"\n                GridPane.rowSpan=\"3\" GridPane.valignment=\"TOP\" GridPane.vgrow=\"NEVER\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\" valignment=\"BOTTOM\"/>\n            </rowConstraints>\n\n            <Button text=\"%Button.HistoricalFill\" onAction=\"#handleHistoricalFill\" GridPane.columnSpan=\"2\"\n                    maxWidth=\"Infinity\"/>\n            <Separator GridPane.rowIndex=\"1\" GridPane.columnSpan=\"2\"/>\n            <GridPane styleClass=\"form\" GridPane.rowIndex=\"2\" GridPane.columnSpan=\"2\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"ALWAYS\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n                <Label text=\"%Label.FillAll\"/>\n                <DecimalTextField fx:id=\"fillAllDecimalTextField\" GridPane.columnIndex=\"1\"/>\n                <Button text=\"%Button.Enter\" onAction=\"#handleFillAllAction\" GridPane.columnIndex=\"2\"/>\n            </GridPane>\n            <Separator GridPane.rowIndex=\"3\" GridPane.columnSpan=\"2\"/>\n\n            <Label text=\"%Label.Pattern\" GridPane.rowIndex=\"4\"/>\n            <ComboBox fx:id=\"patternComboBox\" GridPane.rowIndex=\"4\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.StartRow\" GridPane.rowIndex=\"5\"/>\n            <Spinner fx:id=\"startRowSpinner\" GridPane.rowIndex=\"5\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.EndRow\" GridPane.rowIndex=\"6\"/>\n            <Spinner fx:id=\"endRowSpinner\" GridPane.rowIndex=\"6\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.Amount\" GridPane.rowIndex=\"7\"/>\n            <DecimalTextField fx:id=\"fillPatternAmountDecimalTextField\" GridPane.rowIndex=\"7\" GridPane.columnIndex=\"1\"\n                              maxWidth=\"Infinity\"/>\n\n            <ButtonBar GridPane.rowIndex=\"8\" GridPane.columnSpan=\"2\">\n                <buttons>\n                    <Button onAction=\"#handlePatternFillAction\" text=\"%Button.Enter\" ButtonBar.buttonData=\"OK_DONE\"/>\n                </buttons>\n            </ButtonBar>\n\n        </GridPane>\n    </TitledPane>\n\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"3\" GridPane.columnSpan=\"3\">\n        <buttons>\n            <Button onAction=\"#handleOkayAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button onAction=\"#handleCloseAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/budget/BudgetManagerDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.control.ListView?>\n\n<GridPane fx:controller=\"jgnash.uifx.views.budget.BudgetManagerDialogController\" minHeight=\"300\" minWidth=\"280\"\n          xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\" valignment=\"TOP\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <ListView fx:id=\"budgetListView\" GridPane.rowSpan=\"5\" prefHeight=\"200\"/>\n\n    <Button text=\"%Button.NewHist\" onAction=\"#handleNewHistoricalAction\"\n            GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\"/>\n    <Button text=\"%Button.NewEmpty\" onAction=\"#handleNewAction\" maxWidth=\"Infinity\"\n            GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n    <Button fx:id=\"duplicateButton\" text=\"%Button.Duplicate\" onAction=\"#handleDuplicateAction\" maxWidth=\"Infinity\"\n            GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n    <Button fx:id=\"renameButton\" text=\"%Button.Rename\" onAction=\"#handleRenameAction\" maxWidth=\"Infinity\"\n            GridPane.rowIndex=\"3\" GridPane.columnIndex=\"1\"/>\n    <Button fx:id=\"deleteButton\" text=\"%Button.Delete\" onAction=\"#handleDeleteAction\" maxWidth=\"Infinity\"\n            GridPane.rowIndex=\"4\" GridPane.columnIndex=\"1\"/>\n\n    <ButtonBar GridPane.rowIndex=\"5\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\" maxWidth=\"Infinity\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/budget/BudgetPropertiesDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.CheckBox?>\n\n<?import javafx.scene.control.Spinner?>\n<GridPane fx:controller=\"jgnash.uifx.views.budget.BudgetPropertiesDialogController\" minHeight=\"-Infinity\"\n          xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.Description\" labelFor=\"$descriptionTextField\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"0\"/>\n    <TextField fx:id=\"descriptionTextField\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.Period\" labelFor=\"$periodComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"0\"/>\n    <ComboBox fx:id=\"periodComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.StartMonth\" labelFor=\"$periodComboBox\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"0\"/>\n    <ComboBox fx:id=\"startMonthComboBox\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n    <TitledPane text=\"%Title.Rounding\" collapsible=\"false\" GridPane.rowIndex=\"3\" GridPane.columnSpan=\"2\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.Scale\" labelFor=\"$scaleSpinner\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"0\"/>\n            <Spinner fx:id=\"scaleSpinner\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.RoundingMode\" labelFor=\"$roundingMethodComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"0\"/>\n            <ComboBox fx:id=\"roundingMethodComboBox\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n        </GridPane>\n\n    </TitledPane>\n\n    <TitledPane text=\"%Title.AccountGroups\" collapsible=\"false\" GridPane.rowIndex=\"4\" GridPane.columnSpan=\"2\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n            </rowConstraints>\n\n            <CheckBox fx:id=\"incomeCheckBox\" text=\"%Button.IncomeAccounts\" GridPane.rowIndex=\"0\"/>\n            <CheckBox fx:id=\"expenseCheckBox\" text=\"%Button.ExpenseAccounts\" GridPane.rowIndex=\"1\"/>\n            <CheckBox fx:id=\"assetCheckBox\" text=\"%Button.AssetAccounts\" GridPane.rowIndex=\"2\"/>\n            <CheckBox fx:id=\"liabilityCheckBox\" text=\"%Button.LiabilityAccounts\" GridPane.rowIndex=\"3\"/>\n        </GridPane>\n    </TitledPane>\n\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"5\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button onAction=\"#handleOkayAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button onAction=\"#handleCloseAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/budget/BudgetTable.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ScrollBar?>\n<?import javafx.scene.control.TableView?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Spinner?>\n\n<?import javafx.scene.control.TreeTableView?>\n<?import javafx.scene.control.ToolBar?>\n\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.layout.Pane?>\n<BorderPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n            fx:controller=\"jgnash.uifx.views.budget.BudgetTableController\" styleClass=\"form\">\n    <top>\n        <ToolBar>\n            <Label text=\"%Label.Year\"/>\n            <Spinner fx:id=\"yearSpinner\"/>\n            <Pane prefWidth=\"8\"/>  <!-- Force a little bit of extra space -->\n            <CheckBox text=\"%Button.RunningBalance\" fx:id=\"runningTotalsButton\"/>\n            <Separator/>\n            <HBox fx:id=\"sparkLinePane\" styleClass=\"form\"/>\n        </ToolBar>\n    </top>\n    <center>\n        <GridPane fx:id=\"gridPane\" BorderPane.alignment=\"CENTER\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n                <RowConstraints vgrow=\"NEVER\" minHeight=\"-Infinity\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n            <TreeTableView fx:id=\"accountTreeView\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"0\" minWidth=\"-Infinity\"/>\n            <TableView fx:id=\"periodTable\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"1\"/>\n            <TableView fx:id=\"accountSummaryTable\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"2\"/>\n            <ScrollBar fx:id=\"verticalScrollBar\" orientation=\"VERTICAL\" GridPane.rowIndex=\"0\" GridPane.columnIndex=\"3\"/>\n\n            <TableView fx:id=\"accountTypeTable\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"0\" minWidth=\"-Infinity\"/>\n            <TableView fx:id=\"periodSummaryTable\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n            <TableView fx:id=\"accountGroupPeriodSummaryTable\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"2\"/>\n\n            <ScrollBar fx:id=\"horizontalScrollBar\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n        </GridPane>\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/budget/BudgetView.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n<BorderPane fx:id=\"borderPane\" xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.budget.BudgetViewController\">\n    <top>\n        <ToolBar>\n            <ComboBox fx:id=\"availableBudgetsComboBox\"/>\n            <Button fx:id=\"propertiesButton\" text=\"%Button.Properties\" onAction=\"#handlePropertiesAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"EDIT\"/>\n                </graphic>\n            </Button>\n            <Button text=\"%Button.BudgetMgr\" onAction=\"#handleManagerAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE\"/>\n                </graphic>\n            </Button>\n            <Button fx:id=\"exportButton\" text=\"%Button.ExportSpreadsheet\" onAction=\"#handleExportAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE_EXCEL_O\"/>\n                </graphic>\n            </Button>\n            <Button text=\"%Button.Today\" onAction=\"#handleTodayAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CALENDAR\"/>\n                </graphic>\n            </Button>\n        </ToolBar>\n    </top>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/budget/HistoricalBudgetDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.text.TextFlow?>\n\n<GridPane fx:controller=\"jgnash.uifx.views.budget.HistoricalBudgetDialogController\"\n          xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <TextFlow fx:id=\"textFlow\" GridPane.columnSpan=\"2\" GridPane.fillHeight=\"true\"\n              GridPane.vgrow=\"ALWAYS\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.Name\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"0\"/>\n    <TextField fx:id=\"nameTextField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.Period\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"0\"/>\n    <ComboBox fx:id=\"periodComboBox\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"/>\n\n    <CheckBox fx:id=\"roundupCheckBox\" text=\"%Button.RoundToWhole\" GridPane.rowIndex=\"3\" GridPane.columnSpan=\"2\"/>\n\n    <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"4\" GridPane.columnSpan=\"2\">\n        <buttons>\n            <Button fx:id=\"okButton\" onAction=\"#handleOkAction\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"/>\n            <Button onAction=\"#handleCancelAction\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/main/ConsoleDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ProgressBar?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.text.Text?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" minHeight=\"-Infinity\"\n          fx:controller=\"jgnash.uifx.views.main.ConsoleDialogController\"\n          minWidth=\"-Infinity\" prefHeight=\"400.0\" prefWidth=\"600.0\" styleClass=\"dialog, form\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"100.0\" prefHeight=\"200.0\" vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"SOMETIMES\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <TextArea fx:id=\"consoleArea\" editable=\"false\" prefHeight=\"200.0\" prefWidth=\"200.0\"/>\n\n    <StackPane GridPane.rowIndex=\"1\">\n        <ProgressBar fx:id=\"memoryUsageProgressBar\" maxWidth=\"Infinity\"/>\n        <Text fx:id=\"memoryUsageText\"/>\n    </StackPane>\n\n    <ButtonBar GridPane.rowIndex=\"2\">\n        <buttons>\n            <Button text=\"%Button.ForceGC\" onAction=\"#handleForceGarbageCollection\" ButtonBar.buttonUniformSize=\"false\"\n                    ButtonBar.buttonData=\"LEFT\"/>\n            <Button text=\"%Button.CopyToClip\" onAction=\"#handleCopyToClipboard\" ButtonBar.buttonUniformSize=\"false\"\n                    ButtonBar.buttonData=\"LEFT\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CLIPBOARD\"/>\n                </graphic>\n            </Button>\n\n            <Button text=\"%Button.Close\" onAction=\"#handleCloseAction\" ButtonBar.buttonUniformSize=\"false\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/main/MainMenuBar.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Menu?>\n<?import javafx.scene.control.MenuBar?>\n<?import javafx.scene.control.MenuItem?>\n<?import javafx.scene.control.SeparatorMenuItem?>\n<?import javafx.scene.input.KeyCodeCombination?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<MenuBar fx:id=\"menuBar\" id=\"menuBar\" useSystemMenuBar=\"true\" xmlns=\"http://javafx.com/javafx\"\n         xmlns:fx=\"http://javafx.com/fxml\" fx:controller=\"jgnash.uifx.views.main.MenuBarController\">\n\n    <Menu text=\"%Menu.File.Name\" id=\"fileMenu\">\n        <MenuItem onAction=\"#handleNewAction\" text=\"%Menu.New.Name\">\n            <accelerator>\n                <KeyCodeCombination alt=\"UP\" code=\"N\" control=\"UP\" meta=\"UP\" shift=\"UP\" shortcut=\"DOWN\"/>\n            </accelerator>\n            <graphic>\n                <MaterialDesignLabel glyphName=\"FILE\"/>\n            </graphic>\n        </MenuItem>\n        <SeparatorMenuItem/>\n        <MenuItem fx:id=\"openMenuItem\" onAction=\"#handleOpenAction\" text=\"%Menu.Open.Name\">\n            <accelerator>\n                <KeyCodeCombination alt=\"UP\" code=\"O\" control=\"UP\" meta=\"UP\" shift=\"UP\" shortcut=\"DOWN\"/>\n            </accelerator>\n            <graphic>\n                <MaterialDesignLabel glyphName=\"FOLDER_OPEN\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem fx:id=\"saveAsMenuItem\" onAction=\"#handleSaveAsAction\" text=\"%Menu.SaveAs.Name\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"SAVE\"/>\n            </graphic>\n        </MenuItem>\n        <SeparatorMenuItem/>\n        <Menu text=\"%Menu.Import.Name\" id=\"importMenu\">\n            <MenuItem text=\"%Menu.ImportOfx.Name\" fx:id=\"importOfxMenuItem\" onAction=\"#handleImportOFXAction\"/>\n            <MenuItem text=\"%Menu.ImportQif.Name\" fx:id=\"importQifMenuItem\" onAction=\"#handleImportQIFAction\"/>\n            <SeparatorMenuItem/>\n            <MenuItem text=\"%Menu.ImportAccounts.Name\"  fx:id=\"importAccountsMenuItem\" onAction=\"#handleImportAccountsAction\"/>\n        </Menu>\n        <Menu text=\"%Menu.Export.Name\" id=\"exportMenu\">\n            <MenuItem text=\"%Menu.ExportAccounts.Name\"  fx:id=\"exportAccountsMenuItem\" onAction=\"#handleExportAccountsAction\"/>\n        </Menu>\n        <SeparatorMenuItem/>\n        <MenuItem fx:id=\"changePasswordMenuItem\" onAction=\"#handleChangeDatabasePasswordAction\" text=\"%Menu.ChangeCredentials.Name\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"KEY\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem fx:id=\"packDatabaseMenuItem\" onAction=\"#handlePackDatabaseAction\" text=\"%Menu.PackDatabase.Name\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"ARROW_COLLAPSE_VERTICAL\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem fx:id=\"shutdownServerMenuItem\" onAction=\"#handleShutDownServerAction\" text=\"%Menu.ShutdownServer.Name\"/>\n        <SeparatorMenuItem/>\n        <MenuItem fx:id=\"closeMenuItem\" onAction=\"#handleCloseAction\" text=\"%Menu.Close.Name\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"CLOSE_CIRCLE\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem fx:id=\"exitMenuItem\" onAction=\"#handleExitAction\" text=\"%Menu.Exit.Name\">\n            <accelerator>\n                <KeyCodeCombination alt=\"UP\" code=\"Q\" control=\"UP\" meta=\"UP\" shift=\"UP\" shortcut=\"DOWN\"/>\n            </accelerator>\n            <graphic>\n                <MaterialDesignLabel glyphName=\"SIGN_OUT\"/>\n            </graphic>\n        </MenuItem>\n    </Menu>\n\n    <Menu fx:id=\"reportMenu\" id=\"reportMenu\" text=\"%Menu.Reports.Name\">\n        <Menu text=\"%Menu.Export.Name\">\n            <MenuItem text=\"%Menu.ProfitLossTXT.Name\" onAction=\"#handleExportProfitLoss\"/>\n            <MenuItem text=\"%Menu.MonthEndBalanceCSV.Name\" onAction=\"#handleExportBalanceByMonthCSVReport\"/>\n        </Menu>\n        <Menu text=\"%Menu.Charts.Name\">\n            <MenuItem text=\"%Menu.IEBarChart.Name\" onAction=\"#handleIncomeExpenseBarChart\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHART_BAR\"/>\n                </graphic>\n            </MenuItem>\n            <MenuItem text=\"%Menu.IEPieChart.Name\" onAction=\"#handleIncomeExpensePieChart\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHART_PIE\"/>\n                </graphic>\n            </MenuItem>\n            <MenuItem text=\"%Menu.PayeePieChart.Name\" onAction=\"#handleIncomeExpensePayeePieChart\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHART_PIE\"/>\n                </graphic>\n            </MenuItem>\n            <SeparatorMenuItem/>\n            <MenuItem text=\"%Menu.PeriodicAccountBalance.Name\" onAction=\"#handleDisplayAccountBalanceChart\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHART_BAR\"/>\n                </graphic>\n            </MenuItem>\n            <SeparatorMenuItem/>\n            <MenuItem text=\"%Menu.TransactionTagPieChart.Name\" onAction=\"#handleDisplayTransactionTagPieChart\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHART_PIE\"/>\n                </graphic>\n            </MenuItem>\n        </Menu>\n        <Menu text=\"%Menu.Account.Name\">\n            <MenuItem text=\"%Menu.AccountRegister.Name\" onAction=\"#handleDisplayAccountRegisterReport\"/>\n            <MenuItem text=\"%Menu.BalanceSheet.Name\" onAction=\"#handleDisplayBalanceSheetReport\"/>\n            <MenuItem text=\"%Menu.ListOfAccounts.Name\" onAction=\"#handleDisplayListOfAccountsReport\"/>\n            <MenuItem text=\"%Menu.NetWorth.Name\" onAction=\"#handleDisplayNetWorthReport\"/>\n            <MenuItem text=\"%Menu.Portfolio.Name\" fx:id=\"portfolioReportMenuItem\" onAction=\"#handleDisplayPortfolioReport\"/>\n            <MenuItem text=\"%Menu.ProfitLoss.Name\" onAction=\"#handleDisplayProfitLossReport\"/>\n        </Menu>\n    </Menu>\n\n    <Menu text=\"%Menu.View.Name\" id=\"viewMenu\">\n        <Menu fx:id=\"themesMenu\" text=\"%Menu.Themes.Name\"/>\n        <MenuItem text=\"%Menu.CustomStyleSheetApply.Name\" onAction=\"#handleApplyStyleAction\"/>\n        <MenuItem text=\"%Menu.CustomStyleSheetRemove.Name\" onAction=\"#handleClearStyleAction\"/>\n        <SeparatorMenuItem/>\n        <MenuItem text=\"%Menu.FontSize.Name\" onAction=\"#handleFontSizeAction\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"TEXT_HEIGHT\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem text=\"%Menu.BaseColor.Name\" onAction=\"#handleBaseColorAction\"/>\n    </Menu>\n\n    <Menu text=\"%Menu.Tools.Name\" id=\"toolsMenu\">\n        <Menu fx:id=\"securitiesMenu\" text=\"%Menu.Securities.Name\" id=\"securitiesMenu\">\n            <MenuItem text=\"%Menu.ModifyCommodity.Name\"\n                      onAction=\"#handleCreateModifySecuritiesAction\"/>\n            <MenuItem text=\"%Menu.HistoryCommodity.Name\"\n                      onAction=\"#handleSecuritiesHistoryAction\"/>\n            <MenuItem text=\"%Menu.HistoryImport.Name\"\n                      onAction=\"#handleSecurityHistoryImportAction\"/>\n            <SeparatorMenuItem/>\n            <MenuItem text=\"%Menu.BackgroundSecurityUpdate.Name\"\n                      onAction=\"#updateSecurities\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n                </graphic>\n            </MenuItem>\n        </Menu>\n        <Menu fx:id=\"currenciesMenu\" id=\"currenciesMenu\" text=\"%Menu.Currency.Name\">\n            <MenuItem text=\"%Menu.AddRemoveCurrency.Name\" onAction=\"#handleAddRemoveCurrenciesAction\"/>\n            <MenuItem text=\"%Menu.ModifyCurrency.Name\" onAction=\"#handleModifyCurrenciesAction\"/>\n            <MenuItem text=\"%Menu.ModifyExchangeRates.Name\" onAction=\"#handleEditExchangeRatesAction\"/>\n            <MenuItem text=\"%Menu.DefaultCurrency.Name\" onAction=\"#handleSetDefaultCurrencyAction\"/>\n            <SeparatorMenuItem/>\n            <MenuItem text=\"%Menu.BackgroundCurrencyUpdate.Name\" onAction=\"#updateCurrencies\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n                </graphic>\n            </MenuItem>\n        </Menu>\n        <MenuItem fx:id=\"recurringTransactionsMenuItem\" text=\"%Menu.RecurringList.Name\"\n                  onAction=\"#handleShowRecurringTransactionsAction\"/>\n        <SeparatorMenuItem/>\n        <MenuItem fx:id=\"tagManagerMenuItem\" text=\"%Menu.TagManager.Name\" onAction=\"#handleTagManagerAction\"/>\n        <SeparatorMenuItem/>\n        <MenuItem fx:id=\"budgetManagerMenuItem\" text=\"%Menu.BudgetManager.Name\" onAction=\"#handleBudgetManagerAction\"/>\n        <SeparatorMenuItem/>\n        <MenuItem text=\"%Menu.Option.Name\" fx:id=\"optionsMenuItem\" onAction=\"#handleShowOptionDialog\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"COGS\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem text=\"%Menu.EditTranNumList.Name\" fx:id=\"transNumberListMenuItem\"\n                  onAction=\"#handleShowTranNumberListDialog\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LIST\"/>\n            </graphic>\n        </MenuItem>\n        <MenuItem text=\"%Menu.ConfigImportFilters.Name\" fx:id=\"configureTranImportFiltersMenuItem\"\n                  onAction=\"#handleShowTranImportFilterDialog\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"COGS\"/>\n            </graphic>\n        </MenuItem>\n        <SeparatorMenuItem/>\n        <MenuItem text=\"%Menu.Locale.Name\" onAction=\"#changeDefaultLocale\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LANGUAGE\"/>\n            </graphic>\n        </MenuItem>\n        <SeparatorMenuItem/>\n        <MenuItem text=\"%Menu.RunJavaScript.Name\" onAction=\"#handleExecuteJavaScriptFile\">\n            <accelerator>\n                <KeyCodeCombination alt=\"UP\" code=\"J\" control=\"UP\" meta=\"UP\" shift=\"UP\" shortcut=\"DOWN\"/>\n            </accelerator>\n            <graphic>\n                <MaterialDesignLabel glyphName=\"LANGUAGE_JAVASCRIPT\"/>\n            </graphic>\n        </MenuItem>\n    </Menu>\n\n    <Menu fx:id=\"windowMenu\" text=\"%Menu.Window.Name\">\n        <SeparatorMenuItem/>\n        <MenuItem text=\"%Menu.CloseAllWindows.Name\" onAction=\"#closeAllWindows\"/>\n    </Menu>\n\n    <Menu text=\"%Menu.Help.Name\" id=\"helpMenu\">\n        <MenuItem text=\"%Menu.About.Name\" onAction=\"#handleAboutAction\">\n            <accelerator>\n                <KeyCodeCombination alt=\"UP\" code=\"A\" control=\"UP\" meta=\"UP\" shift=\"UP\" shortcut=\"DOWN\"/>\n            </accelerator>\n            <graphic>\n                <MaterialDesignLabel glyphName=\"INFO\"/>\n            </graphic>\n        </MenuItem>\n        <SeparatorMenuItem/>\n        <MenuItem text=\"%Menu.Console.Name\" onAction=\"#handleShowConsoleDialog\">\n            <graphic>\n                <MaterialDesignLabel glyphName=\"CONSOLE\"/>\n            </graphic>\n        </MenuItem>\n    </Menu>\n\n</MenuBar>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/main/MainToolBar.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.control.Tooltip?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<ToolBar xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\"\n         fx:controller=\"jgnash.uifx.views.main.MainToolBarController\">\n    <Button mnemonicParsing=\"false\" onAction=\"#handleOpenAction\" text=\"%Button.Open\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"FOLDER_OPEN\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%Menu.Open.Tooltip\"/>\n        </tooltip>\n    </Button>\n\n    <Button fx:id=\"closeButton\" mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Close\"\n            disable=\"true\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"CLOSE_CIRCLE\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%Menu.Close.Tooltip\"/>\n        </tooltip>\n    </Button>\n\n    <Separator/>\n\n    <Button fx:id=\"updateSecurities\" mnemonicParsing=\"false\" onAction=\"#handleSecuritiesUpdateAction\"\n            text=\"%Menu.BackgroundSecurityUpdate.Name\" disable=\"true\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%Menu.BackgroundSecurityUpdate.Tooltip\"/>\n        </tooltip>\n    </Button>\n\n    <Button fx:id=\"updateCurrencies\" mnemonicParsing=\"false\" onAction=\"#handleCurrenciesUpdateAction\"\n            text=\"%Menu.BackgroundCurrencyUpdate.Name\" disable=\"true\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"CLOUD_DOWNLOAD\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%Menu.BackgroundCurrencyUpdate.Tooltip\"/>\n        </tooltip>\n    </Button>\n</ToolBar>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/main/OpenDatabaseForm.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.PasswordField?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" minHeight=\"-Infinity\"\n          fx:controller=\"jgnash.uifx.views.main.OpenDatabaseController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"-Infinity\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" maxWidth=\"Infinity\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" maxWidth=\"-Infinity\" minWidth=\"-Infinity\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.DatabaseName\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"/>\n    <TextField fx:id=\"localDatabaseField\" editable=\"false\" prefWidth=\"200.0\" GridPane.columnIndex=\"1\"\n               GridPane.rowIndex=\"0\"/>\n    <Button fx:id=\"selectFileButton\" onAction=\"#handleSelectFileAction\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"0\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"ELLIPSIS_H\"/>\n        </graphic>\n    </Button>\n\n    <CheckBox fx:id=\"remoteServerCheckBox\"\n              text=\"%Button.RemoteServer\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"2\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.DatabaseServer\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"2\"/>\n    <TextField fx:id=\"databaseServerField\" prefWidth=\"200.0\" GridPane.columnIndex=\"1\"\n               GridPane.columnSpan=\"2\" GridPane.rowIndex=\"2\"/>\n\n    <Label text=\"%Label.Port\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"3\"/>\n    <IntegerTextField fx:id=\"portField\" prefWidth=\"200.0\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"\n                      GridPane.rowIndex=\"3\"/>\n    <TitledPane collapsible=\"false\" text=\"%Title.FileLoginCredentials\" GridPane.columnIndex=\"0\"\n                GridPane.columnSpan=\"3\" GridPane.hgrow=\"ALWAYS\" GridPane.rowIndex=\"4\">\n        <GridPane>\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.Password\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"/>\n            <PasswordField fx:id=\"passwordField\" prefWidth=\"200.0\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"/>\n        </GridPane>\n    </TitledPane>\n    <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"3\" GridPane.rowIndex=\"5\">\n        <buttons>\n            <Button text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#okAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#cancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/DayTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import java.lang.Double?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<GridPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n          fx:controller=\"jgnash.uifx.views.recurring.DayTabController\" styleClass=\"form, dialog\">\n    <fx:define>\n        <ToggleGroup fx:id=\"buttonGroup\"/>\n        <Double fx:id=\"PICKER_WIDTH\" fx:value=\"110.0\"/>\n    </fx:define>\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.Every\"/>\n    <Spinner fx:id=\"numberSpinner\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Tab.Day\" GridPane.columnIndex=\"2\"/>\n\n    <Label text=\"%Label.EndOn\" GridPane.rowIndex=\"1\"/>\n    <RadioButton fx:id=\"noEndDateToggleButton\" text=\"%Button.NoEndDate\" toggleGroup=\"$buttonGroup\"\n                 GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n    <HBox GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\">\n        <RadioButton fx:id=\"dateToggleButton\" toggleGroup=\"$buttonGroup\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" prefWidth=\"$PICKER_WIDTH\"/>\n    </HBox>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/MonthTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import java.lang.Double?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<GridPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n          fx:controller=\"jgnash.uifx.views.recurring.MonthTabController\" styleClass=\"form, dialog\">\n    <fx:define>\n        <ToggleGroup fx:id=\"buttonGroup\"/>\n        <Double fx:id=\"PICKER_WIDTH\" fx:value=\"110.0\"/>\n    </fx:define>\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.Every\"/>\n    <Spinner fx:id=\"numberSpinner\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Tab.Month\" GridPane.columnIndex=\"2\"/>\n    <Label text=\"%Label.By\" GridPane.columnIndex=\"3\"/>\n    <ComboBox fx:id=\"typeComboBox\" GridPane.columnIndex=\"4\"/>\n\n    <Label text=\"%Label.EndOn\" GridPane.rowIndex=\"1\"/>\n    <RadioButton fx:id=\"noEndDateToggleButton\" text=\"%Button.NoEndDate\" toggleGroup=\"$buttonGroup\"\n                 GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n    <HBox GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\">\n        <RadioButton fx:id=\"dateToggleButton\" toggleGroup=\"$buttonGroup\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" prefWidth=\"$PICKER_WIDTH\"/>\n    </HBox>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/NoneTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.BorderPane?>\n<BorderPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n            fx:controller=\"jgnash.uifx.views.recurring.NoneTabController\" styleClass=\"form\">\n    <center>\n        <Label text=\"%Message.NoRepeat\"/>\n    </center>\n\n</BorderPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/NotificationDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.Scene?>\n<?import javafx.stage.Stage?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.control.TimePeriodComboBox?>\n\n<fx:root type=\"javafx.stage.Stage\" xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n         minWidth=\"680\" minHeight=\"380\">\n    <scene>\n        <Scene>\n            <BorderPane styleClass=\"dialog, form\" prefWidth=\"680\" prefHeight=\"380\">\n                <center>\n                    <GridPane styleClass=\"form\">\n                        <columnConstraints>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"ALWAYS\"/>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"SOMETIMES\"/>\n                        </columnConstraints>\n                        <rowConstraints>\n                            <RowConstraints vgrow=\"ALWAYS\"/>\n                            <RowConstraints vgrow=\"SOMETIMES\"/>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                        </rowConstraints>\n\n                        <TableViewEx fx:id=\"tableView\" GridPane.columnSpan=\"6\" GridPane.fillWidth=\"true\"\n                                     prefHeight=\"200\"/>\n\n                        <Button text=\"%Button.SelectAll\" fx:id=\"selectAllButton\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n                        <Button text=\"%Button.ClearAll\" fx:id=\"clearAllButton\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n                        <Button text=\"%Button.InvertSelection\" fx:id=\"invertButton\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n\n                        <Label text=\"%Label.RemindLater\" GridPane.columnIndex=\"4\" GridPane.rowIndex=\"1\"/>\n                        <TimePeriodComboBox fx:id=\"snoozeComboBox\" GridPane.columnIndex=\"5\" GridPane.rowIndex=\"1\"/>\n\n                        <ButtonBar GridPane.rowIndex=\"2\" GridPane.columnSpan=\"6\">\n                            <buttons>\n                                <Button fx:id=\"cancelButton\" mnemonicParsing=\"false\"\n                                        text=\"%Button.RemindLater\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                                <Button fx:id=\"okButton\" mnemonicParsing=\"false\"\n                                        text=\"%Button.AckSel\" ButtonBar.buttonData=\"OK_DONE\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </GridPane>\n                </center>\n            </BorderPane>\n        </Scene>\n    </scene>\n</fx:root>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/RecurringDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<?import javafx.scene.layout.StackPane?>\n<GridPane fx:controller=\"jgnash.uifx.views.recurring.RecurringDialogController\"\n          xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" styleClass=\"form, dialog\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <StackPane fx:id=\"contentPane\"/>\n\n    <ButtonBar GridPane.rowIndex=\"1\">\n        <buttons>\n            <Button onAction=\"#handleCloseAction\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n\n</GridPane>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/RecurringProperties.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import jgnash.uifx.control.TextFieldEx?>\n\n<VBox xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n      fx:controller=\"jgnash.uifx.views.recurring.RecurringPropertiesController\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.Transaction\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"ALWAYS\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.Account\" GridPane.rowIndex=\"0\"/>\n            <AccountComboBox fx:id=\"accountComboBox\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"\n                             GridPane.columnSpan=\"2\" maxWidth=\"Infinity\"/>\n\n            <Label text=\"%Label.Description\" GridPane.rowIndex=\"1\"/>\n            <TextFieldEx fx:id=\"descriptionTextField\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"\n                         GridPane.columnSpan=\"2\"/>\n\n            <Label text=\"%Label.Transaction\" GridPane.rowIndex=\"2\"/>\n            <TextFieldEx fx:id=\"payeeTextField\" editable=\"false\" GridPane.rowIndex=\"2\"\n                         GridPane.columnIndex=\"1\"/>\n            <ButtonBar GridPane.rowIndex=\"2\" GridPane.columnIndex=\"2\">\n                <buttons>\n                    <Button text=\"%Button.Edit\" onAction=\"#handleEditTransaction\"\n                            ButtonBar.buttonData=\"LEFT\"/>\n                    <Button text=\"%Button.Delete\" ButtonBar.buttonData=\"LEFT\"/>\n                </buttons>\n            </ButtonBar>\n\n            <Label text=\"%Label.Notes\" GridPane.rowIndex=\"3\"/>\n            <TextArea fx:id=\"notesTextArea\" minWidth=\"280.0\" prefHeight=\"80.0\" prefWidth=\"280.0\"\n                      GridPane.rowIndex=\"3\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane animated=\"false\" text=\"%Title.Frequency\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.FirstPayDate\"/>\n            <DatePickerEx fx:id=\"startDatePicker\" GridPane.columnIndex=\"1\" prefWidth=\"110\"/>\n            <CheckBox fx:id=\"enabledCheckBox\" text=\"%Button.Enabled\" GridPane.columnIndex=\"2\"/>\n            <TabPane fx:id=\"tabs\" GridPane.rowIndex=\"1\" GridPane.columnSpan=\"3\"\n                     tabClosingPolicy=\"UNAVAILABLE\"/>\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane animated=\"false\" text=\"%Title.Entry\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <GridPane styleClass=\"form\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <CheckBox fx:id=\"autoEnterCheckBox\" text=\"%Button.EnterDaysBefore\"/>\n            <IntegerTextField fx:id=\"daysBeforeTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"/>\n\n            <Label text=\"%Label.LastOccurrence\" GridPane.rowIndex=\"1\"/>\n            <TextField editable=\"false\" fx:id=\"lastOccurrenceTextField\" GridPane.rowIndex=\"1\"\n                       GridPane.columnIndex=\"1\"/>\n        </GridPane>\n\n    </TitledPane>\n\n    <ButtonBar fx:id=\"buttonBar\" VBox.vgrow=\"NEVER\">\n        <buttons>\n            <Button fx:id=\"okButton\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#okAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#cancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</VBox>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/RecurringView.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.recurring.RecurringViewController\">\n    <top>\n        <ToolBar>\n            <Button mnemonicParsing=\"false\" text=\"%Button.New\" onAction=\"#handleNewAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CALENDAR_PLUS\"/>\n                </graphic>\n            </Button>\n            <Button fx:id=\"modifyButton\" mnemonicParsing=\"false\" text=\"%Button.Modify\" onAction=\"#handleModifyAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"EDIT\"/>\n                </graphic>\n            </Button>\n            <Button fx:id=\"deleteButton\" mnemonicParsing=\"false\" text=\"%Button.Delete\" onAction=\"#handleDeleteAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                </graphic>\n            </Button>\n            <Separator orientation=\"VERTICAL\"/>\n            <Button mnemonicParsing=\"false\" text=\"%Button.CheckReminders\" onAction=\"#handleRefreshAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"REFRESH\"/>\n                </graphic>\n            </Button>\n            <Button fx:id=\"nowButton\" text=\"%Button.ExecuteNow\" onAction=\"#handleNowAction\" mnemonicParsing=\"false\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CALENDAR_CHECK\"/>\n                </graphic>\n            </Button>\n        </ToolBar>\n    </top>\n    <center>\n        <TableViewEx fx:id=\"tableView\"/>\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/WeekTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import java.lang.Double?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<GridPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n          fx:controller=\"jgnash.uifx.views.recurring.WeekTabController\" styleClass=\"form, dialog\">\n    <fx:define>\n        <ToggleGroup fx:id=\"buttonGroup\"/>\n        <Double fx:id=\"PICKER_WIDTH\" fx:value=\"110.0\"/>\n    </fx:define>\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.Every\"/>\n    <Spinner fx:id=\"numberSpinner\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Tab.Week\" GridPane.columnIndex=\"2\"/>\n\n    <Label text=\"%Label.EndOn\" GridPane.rowIndex=\"1\"/>\n    <RadioButton fx:id=\"noEndDateToggleButton\" text=\"%Button.NoEndDate\" toggleGroup=\"$buttonGroup\"\n                 GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n    <HBox GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\">\n        <RadioButton fx:id=\"dateToggleButton\" toggleGroup=\"$buttonGroup\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" prefWidth=\"$PICKER_WIDTH\"/>\n    </HBox>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/recurring/YearTab.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n\n<?import java.lang.Double?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.RadioButton?>\n<?import javafx.scene.control.Spinner?>\n<?import javafx.scene.control.ToggleGroup?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<GridPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n          fx:controller=\"jgnash.uifx.views.recurring.YearTabController\" styleClass=\"form, dialog\">\n    <fx:define>\n        <ToggleGroup fx:id=\"buttonGroup\"/>\n        <Double fx:id=\"PICKER_WIDTH\" fx:value=\"110.0\"/>\n    </fx:define>\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n        <ColumnConstraints hgrow=\"NEVER\" halignment=\"LEFT\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.Every\"/>\n    <Spinner fx:id=\"numberSpinner\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Tab.Year\" GridPane.columnIndex=\"2\"/>\n\n    <Label text=\"%Label.EndOn\" GridPane.rowIndex=\"1\"/>\n    <RadioButton fx:id=\"noEndDateToggleButton\" text=\"%Button.NoEndDate\" toggleGroup=\"$buttonGroup\"\n                 GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n    <HBox GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\">\n        <RadioButton fx:id=\"dateToggleButton\" toggleGroup=\"$buttonGroup\"/>\n        <DatePickerEx fx:id=\"endDatePicker\" prefWidth=\"$PICKER_WIDTH\"/>\n    </HBox>\n</GridPane>"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/AccountExchangePane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.PopOverButton?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<fx:root type=\"javafx.scene.layout.GridPane\" maxHeight=\"-Infinity\" maxWidth=\"Infinity\" minHeight=\"-Infinity\"\n         minWidth=\"-Infinity\"\n         xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <AccountComboBox fx:id=\"accountCombo\" maxWidth=\"Infinity\" minWidth=\"50\" prefWidth=\"100\"/>\n    <Label fx:id=\"label\" text=\"%Label.ExchangeAmount\" GridPane.columnIndex=\"1\"/>\n    <DecimalTextField fx:id=\"exchangeAmountField\" GridPane.columnIndex=\"2\" minWidth=\"80\" maxWidth=\"80\"/>\n    <PopOverButton fx:id=\"expandButton\" GridPane.columnIndex=\"3\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"SWAP_HORIZONTAL\"/>\n        </graphic>\n        <contentNode>\n            <GridPane maxHeight=\"-Infinity\" maxWidth=\"-Infinity\" minHeight=\"-Infinity\" minWidth=\"-Infinity\"\n                      styleClass=\"form\">\n                <columnConstraints>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"NEVER\"/>\n                    <ColumnConstraints hgrow=\"SOMETIMES\" maxWidth=\"-Infinity\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n                </columnConstraints>\n                <rowConstraints>\n                    <RowConstraints vgrow=\"NEVER\"/>\n                </rowConstraints>\n\n                <Label fx:id=\"exchangeLabel\" text=\"Label\"/>\n                <Label text=\"%Label.ExchangeRate\" GridPane.columnIndex=\"1\"/>\n                <DecimalTextField fx:id=\"exchangeRateField\" GridPane.columnIndex=\"2\"/>\n            </GridPane>\n        </contentNode>\n    </PopOverButton>\n</fx:root>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/AdjustSharesSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.AdjustSharesSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Price\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <GridPane styleClass=\"form\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" GridPane.fillWidth=\"true\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <DecimalTextField fx:id=\"priceField\" GridPane.columnIndex=\"0\"/>\n        <Label text=\"%Label.Quantity\" GridPane.columnIndex=\"1\"/>\n        <DecimalTextField fx:id=\"quantityField\" GridPane.columnIndex=\"2\"/>\n\n    </GridPane>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Total\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"totalField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"4\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"0\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/AdjustmentSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.AdjustmentSlipController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.Payee\" GridPane.rowIndex=\"0\"/>\n    <AutoCompleteTextField fx:id=\"payeeTextField\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <GridPane GridPane.columnSpan=\"2\" GridPane.halignment=\"LEFT\" GridPane.hgrow=\"NEVER\" GridPane.rowIndex=\"2\"\n              GridPane.vgrow=\"NEVER\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n    <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"4\" GridPane.rowIndex=\"3\">\n        <buttons>\n            <Button fx:id=\"convertButton\" ButtonBar.buttonData=\"LEFT\" onAction=\"#convertAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"ADJUST\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ConvertSEntry\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"enterButton\" text=\"%Button.Enter\" ButtonBar.buttonData=\"OK_DONE\"\n                    onAction=\"#handleEnterAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#handleCancelAction\"/>\n        </buttons>\n    </ButtonBar>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/AmortizeSetupDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.AccountComboBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.IntegerTextField?>\n<?import jgnash.uifx.control.TextFieldEx?>\n\n<VBox xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n      fx:controller=\"jgnash.uifx.views.register.AmortizeSetupDialogController\" styleClass=\"dialog, form\">\n\n    <TitledPane text=\"%Title.AmortizationSetup\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n                <ColumnConstraints hgrow=\"ALWAYS\" />\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.AnIntRate\"/>\n            <DecimalTextField fx:id=\"interestField\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.OrigLoanAmt\" GridPane.rowIndex=\"1\"/>\n            <DecimalTextField fx:id=\"loanAmountField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n\n            <Label text=\"%Label.LoanTerm\" GridPane.rowIndex=\"2\"/>\n            <IntegerTextField fx:id=\"loanTermField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n\n            <Label text=\"%Label.PayPerTerm\" GridPane.rowIndex=\"3\"/>\n            <IntegerTextField  fx:id=\"payPeriodsField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\"/>\n\n            <Label text=\"%Label.CompPerTerm\" GridPane.rowIndex=\"4\"/>\n            <IntegerTextField  fx:id=\"intPeriodsField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"4\"/>\n\n            <Label text=\"%Label.FirstPayDate\" GridPane.rowIndex=\"5\"/>\n            <DatePickerEx fx:id=\"dateField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"5\"/>\n\n            <Label text=\"%Label.EscrowPmi\" GridPane.rowIndex=\"6\"/>\n            <DecimalTextField fx:id=\"feesField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"6\"/>\n\n            <CheckBox fx:id=\"useDaysButton\" text=\"%Button.UseDailyRate\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"7\"/>\n\n            <Label text=\"%Label.CompDaysPerYear\" GridPane.rowIndex=\"8\"/>\n            <DecimalTextField fx:id=\"daysField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"8\"/>\n\n        </GridPane>\n    </TitledPane>\n\n    <TitledPane animated=\"false\" text=\"%Title.TransactionSetup\" VBox.vgrow=\"ALWAYS\" collapsible=\"false\">\n        <GridPane styleClass=\"form, dialog\">\n            <columnConstraints>\n                <ColumnConstraints hgrow=\"ALWAYS\"/>\n                <ColumnConstraints hgrow=\"NEVER\"/>\n            </columnConstraints>\n            <rowConstraints>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n                <RowConstraints vgrow=\"NEVER\"/>\n            </rowConstraints>\n\n            <Label text=\"%Label.InterestAccount\"/>\n            <AccountComboBox fx:id=\"interestAccountCombo\" maxWidth=\"200\" GridPane.columnIndex=\"1\"/>\n\n            <Label text=\"%Label.BankAccount\" GridPane.rowIndex=\"1\"/>\n            <AccountComboBox fx:id=\"bankAccountCombo\" maxWidth=\"200\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n\n            <Label text=\"%Label.FeesAccount\" GridPane.rowIndex=\"2\"/>\n            <AccountComboBox fx:id=\"feesAccountCombo\" maxWidth=\"200\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n\n            <Label text=\"%Label.Payee\" GridPane.rowIndex=\"3\"/>\n            <TextFieldEx  fx:id=\"payeeField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\"/>\n\n            <Label text=\"%Label.Memo\" GridPane.rowIndex=\"4\"/>\n            <TextFieldEx  fx:id=\"memoField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"4\"/>\n\n        </GridPane>\n    </TitledPane>\n\n    <ButtonBar VBox.vgrow=\"NEVER\">\n        <buttons>\n            <Button text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#okAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#cancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</VBox>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/AttachmentPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<fx:root type=\"javafx.scene.layout.GridPane\" maxHeight=\"-Infinity\" maxWidth=\"Infinity\" minHeight=\"-Infinity\"\n         minWidth=\"-Infinity\"\n         xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Button fx:id=\"attachmentButton\" mnemonicParsing=\"false\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"LINK\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%ToolTip.AddAttachment\"/>\n        </tooltip>\n    </Button>\n    <Button fx:id=\"viewAttachmentButton\" mnemonicParsing=\"false\" GridPane.columnIndex=\"1\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"EYE\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%ToolTip.ViewAttachment\"/>\n        </tooltip>\n    </Button>\n    <Button fx:id=\"deleteAttachmentButton\" mnemonicParsing=\"false\" GridPane.columnIndex=\"2\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"LINK_OFF\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%ToolTip.DeleteAttachment\"/>\n        </tooltip>\n    </Button>\n</fx:root>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/BankSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.SlipController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.Payee\"/>\n    <AutoCompleteTextField fx:id=\"payeeTextField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"3\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"4\"/>\n    <Label text=\"%Label.Account\" GridPane.rowIndex=\"1\"/>\n    <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"\n                         GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n    <Button fx:id=\"splitsButton\" onAction=\"#splitsAction\" mnemonicParsing=\"false\" text=\"%Button.Splits\"\n            GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"4\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"4\" GridPane.rowIndex=\"2\"/>\n    <GridPane GridPane.columnSpan=\"2\" GridPane.halignment=\"LEFT\" GridPane.hgrow=\"NEVER\" GridPane.rowIndex=\"3\"\n              GridPane.vgrow=\"NEVER\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n    <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"2\" GridPane.columnSpan=\"3\" GridPane.rowIndex=\"3\">\n        <buttons>\n            <Button fx:id=\"enterButton\" text=\"%Button.Enter\" ButtonBar.buttonData=\"OK_DONE\"\n                    onAction=\"#handleEnterAction\"/>\n            <Button fx:id=\"cancelButton\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"\n                    onAction=\"#handleCancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/BasicRegisterPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.register.BankRegisterPaneController\">\n    <center>\n        <StackPane fx:id=\"registerTablePane\" VBox.vgrow=\"ALWAYS\"/>\n    </center>\n    <bottom>\n        <TitledPane fx:id=\"titledPane\" contentDisplay=\"GRAPHIC_ONLY\" focusTraversable=\"false\">\n            <graphic>\n                <ButtonBar>\n                    <buttons>\n                        <Button fx:id=\"newButton\" mnemonicParsing=\"false\" text=\"%Button.New\"\n                                ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"duplicateButton\" mnemonicParsing=\"false\" onAction=\"#handleDuplicateAction\"\n                                text=\"%Button.Duplicate\" ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH_MULTIPLE\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"jumpButton\" mnemonicParsing=\"false\" onAction=\"#handleJumpAction\"\n                                text=\"%Button.Jump\" ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"EXTERNAL_LINK\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"deleteButton\" mnemonicParsing=\"false\" onAction=\"#handleDeleteAction\"\n                                text=\"%Button.Delete\" ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                            </graphic>\n                        </Button>\n                    </buttons>\n                </ButtonBar>\n            </graphic>\n            <TabPane fx:id=\"transactionForms\"/>\n        </TitledPane>\n    </bottom>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/BasicRegisterTable.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.register.BasicRegisterTableController\">\n    <top>\n        <TitledPane focusTraversable=\"false\" expanded=\"false\">\n            <graphic>\n                <GridPane maxWidth=\"Infinity\" styleClass=\"info-bar\">\n                    <columnConstraints>\n                        <ColumnConstraints halignment=\"LEFT\" hgrow=\"ALWAYS\"/>\n                        <ColumnConstraints fillWidth=\"false\" halignment=\"LEFT\" minWidth=\"15.0\" prefWidth=\"15.0\"/>\n                        <ColumnConstraints halignment=\"RIGHT\" hgrow=\"NEVER\"/>\n                        <ColumnConstraints halignment=\"LEFT\" hgrow=\"NEVER\"/>\n                        <ColumnConstraints fillWidth=\"false\" halignment=\"LEFT\" minWidth=\"15.0\" prefWidth=\"15.0\"/>\n                        <ColumnConstraints halignment=\"RIGHT\" hgrow=\"NEVER\"/>\n                        <ColumnConstraints halignment=\"LEFT\" hgrow=\"NEVER\"/>\n                    </columnConstraints>\n                    <Label fx:id=\"accountNameLabel\" maxWidth=\"Infinity\"/>\n                    <Label text=\"%Label.Balance\" GridPane.columnIndex=\"2\" GridPane.halignment=\"RIGHT\"/>\n                    <Label fx:id=\"balanceLabel\" GridPane.columnIndex=\"3\"/>\n                    <Label text=\"%Label.ReconciledBalance\" GridPane.columnIndex=\"5\"/>\n                    <Label fx:id=\"reconciledBalanceLabel\" GridPane.columnIndex=\"6\"/>\n                </GridPane>\n            </graphic>\n            <ToolBar>\n                <MaterialDesignLabel glyphName=\"FILTER\"/>\n\n                <ComboBox fx:id=\"reconciledStateFilterComboBox\">\n                    <tooltip>\n                        <Tooltip text=\"%ToolTip.FilterReconciledState\"/>\n                    </tooltip>\n                </ComboBox>\n                <ComboBox fx:id=\"transactionAgeFilterComboBox\">\n                    <tooltip>\n                        <Tooltip text=\"%ToolTip.FilterTransactionAge\"/>\n                    </tooltip>\n                </ComboBox>\n\n                <TextField fx:id=\"payeeFilterTextField\" promptText=\"%ToolTip.FilterPayee\"/>\n                <TextField fx:id=\"memoFilterTextField\" promptText=\"%ToolTip.FilterMemo\"/>\n\n                <TransactionTagPane fx:id=\"tagPane\"/>\n\n                <Button text=\"%Button.ResetAll\" onAction=\"#handleResetFilters\"/>\n            </ToolBar>\n        </TitledPane>\n    </top>\n    <center>\n        <TableViewEx fx:id=\"tableView\" BorderPane.alignment=\"CENTER\"/>\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/BuyShareSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.FeePane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.BuyShareSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Price\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <GridPane styleClass=\"form\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" GridPane.fillWidth=\"true\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <DecimalTextField fx:id=\"priceField\" GridPane.columnIndex=\"0\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Quantity\" GridPane.columnIndex=\"1\"/>\n        <DecimalTextField fx:id=\"quantityField\" GridPane.columnIndex=\"2\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Fees\" GridPane.columnIndex=\"3\"/>\n        <FeePane fx:id=\"feePane\" GridPane.columnIndex=\"4\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n\n    </GridPane>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Total\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"totalField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <Label text=\"%Label.Account\" GridPane.rowIndex=\"3\"/>\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"3\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"1\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"3\"/>\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/DateTransNumberDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n\n<VBox xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n      fx:controller=\"jgnash.uifx.views.register.DateTransNumberDialogController\" styleClass=\"dialog, form\">\n\n    <GridPane styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <Label text=\"%Label.Date\"/>\n        <DatePickerEx fx:id=\"dateField\" GridPane.columnIndex=\"1\"/>\n\n        <Label text=\"%Label.Number\" GridPane.rowIndex=\"1\"/>\n        <TransactionNumberComboBox fx:id=\"transactionNumberField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n    </GridPane>\n\n    <ButtonBar VBox.vgrow=\"NEVER\">\n        <buttons>\n            <Button text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#okAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#cancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</VBox>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/DividendSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.DividendSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.IncomeAccount\" GridPane.rowIndex=\"2\"/>\n    <AccountExchangePane fx:id=\"incomeExchangePane\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                         GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Dividend\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"decimalTextField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <Label text=\"%Label.Account\" GridPane.rowIndex=\"3\"/>\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"3\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"1\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"3\"/>\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/FeeDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import javafx.scene.Scene?>\n<?import javafx.stage.Stage?>\n<?import jgnash.uifx.control.TableViewEx?>\n\n<fx:root type=\"javafx.stage.Stage\" xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" minWidth=\"680\" minHeight=\"440\">\n    <scene>\n        <Scene>\n            <BorderPane>\n                <center>\n                    <VBox styleClass=\"dialog, form\">\n                        <TableViewEx fx:id=\"tableView\" VBox.vgrow=\"ALWAYS\"/>\n                        <ButtonBar VBox.vgrow=\"NEVER\">\n                            <buttons>\n                                <Button fx:id=\"newButton\" text=\"%Button.New\" ButtonBar.buttonData=\"LEFT\"/>\n                                <Button fx:id=\"deleteButton\" text=\"%Button.Delete\" ButtonBar.buttonData=\"LEFT\"/>\n                                <Button visible=\"false\" ButtonBar.buttonData=\"BIG_GAP\"/>\n                                <Button fx:id=\"deleteAllButton\" text=\"%Button.DeleteAll\" ButtonBar.buttonData=\"RIGHT\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </VBox>\n                </center>\n                <bottom>\n                    <VBox styleClass=\"dialog, form\">\n                        <StackPane fx:id=\"formPane\" VBox.vgrow=\"NEVER\" styleClass=\"border\"/>\n                        <ButtonBar VBox.vgrow=\"NEVER\">\n                            <buttons>\n                                <Button fx:id=\"closeButton\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </VBox>\n                </bottom>\n            </BorderPane>\n        </Scene>\n    </scene>\n</fx:root>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/FeeTransactionEntrySlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.FeeTransactionEntrySlipController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"95\" maxWidth=\"95\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.Account\"/>\n    <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.columnIndex=\"1\" GridPane.hgrow=\"ALWAYS\"\n                         GridPane.fillWidth=\"true\"/>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"3\"/>\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <GridPane GridPane.rowIndex=\"2\" GridPane.columnSpan=\"4\" GridPane.fillWidth=\"true\" GridPane.vgrow=\"NEVER\"\n              styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"2\"/>\n        <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"3\">\n            <buttons>\n                <Button text=\"%Button.Enter\" fx:id=\"enterButton\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#handleEnterAction\"/>\n                <Button text=\"%Button.Clear\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#handleCancelAction\"/>\n            </buttons>\n        </ButtonBar>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/GainLossDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import javafx.scene.Scene?>\n<?import javafx.stage.Stage?>\n<?import jgnash.uifx.control.TableViewEx?>\n\n<fx:root type=\"javafx.stage.Stage\" xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" minWidth=\"680\" minHeight=\"440\">\n    <scene>\n        <Scene>\n            <BorderPane>\n                <center>\n                    <VBox styleClass=\"dialog, form\">\n                        <TableViewEx fx:id=\"tableView\" VBox.vgrow=\"ALWAYS\"/>\n                        <ButtonBar VBox.vgrow=\"NEVER\">\n                            <buttons>\n                                <Button fx:id=\"newButton\" text=\"%Button.New\" ButtonBar.buttonData=\"LEFT\"/>\n                                <Button fx:id=\"deleteButton\" text=\"%Button.Delete\" ButtonBar.buttonData=\"LEFT\"/>\n                                <Button visible=\"false\" ButtonBar.buttonData=\"BIG_GAP\"/>\n                                <Button fx:id=\"deleteAllButton\" text=\"%Button.DeleteAll\" ButtonBar.buttonData=\"RIGHT\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </VBox>\n                </center>\n                <bottom>\n                    <VBox styleClass=\"dialog, form\">\n                        <StackPane fx:id=\"formPane\" VBox.vgrow=\"NEVER\" styleClass=\"border\"/>\n                        <ButtonBar VBox.vgrow=\"NEVER\">\n                            <buttons>\n                                <Button fx:id=\"closeButton\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </VBox>\n                </bottom>\n            </BorderPane>\n        </Scene>\n    </scene>\n</fx:root>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/GainLossTransactionEntrySlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.GainLossTransactionEntrySlipController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"95\" maxWidth=\"95\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Label text=\"%Label.Account\"/>\n    <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.columnIndex=\"1\" GridPane.hgrow=\"ALWAYS\"\n                         GridPane.fillWidth=\"true\"/>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"3\"/>\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <GridPane GridPane.rowIndex=\"2\" GridPane.columnSpan=\"4\" GridPane.fillWidth=\"true\" GridPane.vgrow=\"NEVER\"\n              styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"2\"/>\n        <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"3\">\n            <buttons>\n                <Button text=\"%Button.Enter\" fx:id=\"enterButton\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#handleEnterAction\"/>\n                <Button text=\"%Button.Clear\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#handleCancelAction\"/>\n            </buttons>\n        </ButtonBar>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/InvestmentRegisterPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.geometry.Insets?>\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.register.InvestmentRegisterPaneController\">\n    <center>\n        <StackPane fx:id=\"registerTablePane\" VBox.vgrow=\"ALWAYS\"/>\n    </center>\n    <bottom>\n        <TitledPane fx:id=\"titledPane\" contentDisplay=\"GRAPHIC_ONLY\">\n            <graphic>\n                <ButtonBar>\n                    <buttons>\n                        <Button fx:id=\"newButton\" mnemonicParsing=\"false\" text=\"%Button.New\"\n                                ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"duplicateButton\" mnemonicParsing=\"false\" onAction=\"#handleDuplicateAction\"\n                                text=\"%Button.Duplicate\" ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH_MULTIPLE\"/>\n                            </graphic>\n                        </Button>\n                        <Button mnemonicParsing=\"false\" onAction=\"#handleSecuritiesAction\"\n                                text=\"%Button.AvailSecurities\" ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"LIST\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"deleteButton\" mnemonicParsing=\"false\" onAction=\"#handleDeleteAction\"\n                                text=\"%Button.Delete\" ButtonBar.buttonUniformSize=\"false\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                            </graphic>\n                        </Button>\n                    </buttons>\n                </ButtonBar>\n            </graphic>\n            <BorderPane styleClass=\"form, dialog\">\n                <center>\n                    <StackPane fx:id=\"transactionSlips\"/>\n                </center>\n                <bottom>\n                    <GridPane styleClass=\"form\">\n                        <padding>\n                            <Insets top=\"10.0\"/>\n                        </padding>\n                        <columnConstraints>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"ALWAYS\"/>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                        </columnConstraints>\n                        <rowConstraints>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                        </rowConstraints>\n\n                        <Separator GridPane.columnSpan=\"3\"/>\n                        <Label text=\"%Label.Action\" GridPane.rowIndex=\"1\"/>\n                        <ComboBox fx:id=\"actionComboBox\" maxWidth=\"Infinity\" minWidth=\"50\"\n                                  prefWidth=\"100\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\" />\n                        <ButtonBar fx:id=\"buttonBar\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"2\">\n                            <buttons>\n                                <Button text=\"%Button.Enter\" fx:id=\"enterButton\" ButtonBar.buttonData=\"OK_DONE\"\n                                        onAction=\"#handleEnterAction\"/>\n                                <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"\n                                        onAction=\"#handleCancelAction\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </GridPane>\n                </bottom>\n            </BorderPane>\n        </TitledPane>\n    </bottom>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/InvestmentRegisterTable.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import jgnash.uifx.control.TableViewEx?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.register.InvestmentRegisterTableController\">\n   <top>\n       <TitledPane focusTraversable=\"false\" expanded=\"false\">\n           <graphic>\n               <GridPane BorderPane.alignment=\"CENTER\" styleClass=\"info-bar\">\n                   <columnConstraints>\n                       <ColumnConstraints halignment=\"LEFT\" hgrow=\"ALWAYS\"/>\n                       <ColumnConstraints fillWidth=\"false\" halignment=\"LEFT\" hgrow=\"NEVER\" maxWidth=\"-Infinity\" minWidth=\"10.0\"\n                                          prefWidth=\"100.0\"/>\n                       <ColumnConstraints halignment=\"RIGHT\" hgrow=\"NEVER\"/>\n                       <ColumnConstraints halignment=\"LEFT\" hgrow=\"NEVER\"/>\n                       <ColumnConstraints fillWidth=\"false\" halignment=\"LEFT\" hgrow=\"NEVER\" minWidth=\"15.0\" prefWidth=\"15.0\"/>\n                       <ColumnConstraints halignment=\"RIGHT\" hgrow=\"NEVER\"/>\n                       <ColumnConstraints halignment=\"LEFT\" hgrow=\"NEVER\"/>\n                       <ColumnConstraints fillWidth=\"false\" halignment=\"LEFT\" hgrow=\"NEVER\" minWidth=\"15.0\" prefWidth=\"15.0\"/>\n                       <ColumnConstraints halignment=\"RIGHT\" hgrow=\"NEVER\"/>\n                       <ColumnConstraints halignment=\"LEFT\" hgrow=\"NEVER\"/>\n                   </columnConstraints>\n                   <Label fx:id=\"accountNameLabel\" GridPane.hgrow=\"ALWAYS\"/>\n                   <Label text=\"%Label.Balance\" GridPane.columnIndex=\"2\" GridPane.halignment=\"RIGHT\" GridPane.hgrow=\"NEVER\"/>\n                   <Label fx:id=\"balanceLabel\" GridPane.columnIndex=\"3\" GridPane.hgrow=\"NEVER\"/>\n                   <Label text=\"%Label.MarketValue\" GridPane.columnIndex=\"5\"/>\n                   <Label fx:id=\"marketValueLabel\" GridPane.columnIndex=\"6\"/>\n                   <Label text=\"%Label.CashBalance\" GridPane.columnIndex=\"8\"/>\n                   <Label fx:id=\"cashBalanceLabel\" GridPane.columnIndex=\"9\"/>\n               </GridPane>\n           </graphic>\n           <ToolBar>\n               <MaterialDesignLabel glyphName=\"FILTER\"/>\n               <ComboBox fx:id=\"reconciledStateFilterComboBox\">\n                   <tooltip>\n                       <Tooltip text=\"%ToolTip.FilterReconciledState\"/>\n                   </tooltip>\n               </ComboBox>\n               <ComboBox fx:id=\"transactionAgeFilterComboBox\">\n                   <tooltip>\n                       <Tooltip text=\"%ToolTip.FilterTransactionAge\"/>\n                   </tooltip>\n               </ComboBox>\n\n               <TransactionTagPane fx:id=\"tagPane\"/>\n\n               <Button text=\"%Button.ResetAll\" onAction=\"#handleResetFilters\"/>\n           </ToolBar>\n       </TitledPane>\n   </top>\n   <center>\n      <TableViewEx fx:id=\"tableView\" BorderPane.alignment=\"CENTER\" />\n   </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/InvestmentTransactionDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.Scene?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Button?>\n\n<fx:root type=\"javafx.stage.Stage\" xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n         minWidth=\"705\" minHeight=\"150\">\n    <scene>\n        <Scene>\n            <BorderPane styleClass=\"dialog, form\">\n                <center>\n                    <StackPane fx:id=\"transactionSlips\"/>\n                </center>\n                <bottom>\n                    <GridPane styleClass=\"dialog, form\">\n                        <columnConstraints>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                            <ColumnConstraints hgrow=\"ALWAYS\"/>\n                            <ColumnConstraints hgrow=\"NEVER\"/>\n                        </columnConstraints>\n                        <rowConstraints>\n                            <RowConstraints vgrow=\"NEVER\"/>\n                        </rowConstraints>\n                        <Label text=\"%Label.Action\"/>\n                        <ComboBox fx:id=\"actionComboBox\" GridPane.columnIndex=\"1\" maxWidth=\"Infinity\" minWidth=\"50\"\n                                  prefWidth=\"100\"/>\n                        <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"2\">\n                            <buttons>\n                                <Button fx:id=\"enterButton\" text=\"%Button.Enter\" ButtonBar.buttonData=\"OK_DONE\"/>\n                                <Button fx:id=\"cancelButton\" text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </GridPane>\n                </bottom>\n            </BorderPane>\n        </Scene>\n    </scene>\n</fx:root>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/InvestmentTransactionPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.SlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.Payee\"/>\n    <AutoCompleteTextField fx:id=\"payeeTextField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"3\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"4\"/>\n    <Label text=\"%Label.Account\" GridPane.rowIndex=\"1\"/>\n    <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"\n                         GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n    <Button fx:id=\"splitsButton\" onAction=\"#splitsAction\" mnemonicParsing=\"false\" text=\"%Button.Splits\"\n            GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"4\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"2\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"4\" GridPane.rowIndex=\"2\"/>\n    <GridPane GridPane.columnSpan=\"2\" GridPane.halignment=\"LEFT\" GridPane.hgrow=\"NEVER\" GridPane.rowIndex=\"3\"\n              GridPane.vgrow=\"NEVER\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/LiabilityRegisterPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.register.LiabilityRegisterPaneController\">\n    <center>\n        <StackPane fx:id=\"registerTablePane\" VBox.vgrow=\"ALWAYS\"/>\n    </center>\n    <bottom>\n        <TitledPane fx:id=\"titledPane\" contentDisplay=\"GRAPHIC_ONLY\" focusTraversable=\"false\" maxWidth=\"Infinity\">\n            <graphic>\n                <ButtonBar fx:id=\"buttonBar\">\n                    <buttons>\n                        <Button fx:id=\"newButton\" mnemonicParsing=\"false\" text=\"%Button.New\"\n                                ButtonBar.buttonUniformSize=\"false\" ButtonBar.buttonData=\"LEFT\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"duplicateButton\" mnemonicParsing=\"false\" onAction=\"#handleDuplicateAction\"\n                                text=\"%Button.Duplicate\" ButtonBar.buttonUniformSize=\"false\" ButtonBar.buttonData=\"LEFT\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH_MULTIPLE\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"jumpButton\" mnemonicParsing=\"false\" onAction=\"#handleJumpAction\"\n                                text=\"%Button.Jump\" ButtonBar.buttonUniformSize=\"false\" ButtonBar.buttonData=\"LEFT\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"EXTERNAL_LINK\"/>\n                            </graphic>\n                        </Button>\n                        <Button fx:id=\"deleteButton\" mnemonicParsing=\"false\" onAction=\"#handleDeleteAction\"\n                                text=\"%Button.Delete\" ButtonBar.buttonUniformSize=\"false\" ButtonBar.buttonData=\"LEFT\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"TRASH_O\"/>\n                            </graphic>\n                        </Button>\n                        <Button text=\"%Button.NewPayment\" onAction=\"#handleNewPaymentAction\"\n                                ButtonBar.buttonUniformSize=\"false\" ButtonBar.buttonData=\"RIGHT\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"CASH\"/>\n                            </graphic>\n                        </Button>\n                        <Button text=\"%Button.Amortize\" onAction=\"#handleAmortizeAction\"\n                                ButtonBar.buttonUniformSize=\"false\" ButtonBar.buttonData=\"RIGHT\">\n                            <graphic>\n                                <MaterialDesignLabel glyphName=\"BANK\"/>\n                            </graphic>\n                        </Button>\n                    </buttons>\n                </ButtonBar>\n            </graphic>\n            <TabPane fx:id=\"transactionForms\"/>\n        </TitledPane>\n    </bottom>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/LockedBasicRegisterPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n            fx:controller=\"jgnash.uifx.views.register.LockedBasicRegisterPaneController\">\n    <center>\n        <StackPane fx:id=\"registerTablePane\" />\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/LockedInvestmentRegisterPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\"\n            fx:controller=\"jgnash.uifx.views.register.LockedInvestmentRegisterPaneController\">\n    <center>\n        <StackPane fx:id=\"registerTablePane\" />\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/RegisterView.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.SplitPane?>\n<?import javafx.scene.control.ToolBar?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.control.TreeView?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<BorderPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n            fx:controller=\"jgnash.uifx.views.register.RegisterViewController\">\n    <top>\n        <ToolBar>\n            <Button fx:id=\"reconcileButton\" mnemonicParsing=\"false\" text=\"%Button.Reconcile\"\n                    onAction=\"#handleReconcileAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"HANDSHAKE\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ReconcileAccount\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"filterButton\" mnemonicParsing=\"false\" onAction=\"#handleFilterAccountAction\"\n                    text=\"%Button.Filter\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILTER\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.FilterAccounts\"/>\n                </tooltip>\n            </Button>\n            <Button fx:id=\"zoomButton\" mnemonicParsing=\"false\" text=\"%Button.Zoom\" onAction=\"#handleZoomAction\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE_DOCUMENT_BOX_PLUS\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ZoomRegister\"/>\n                </tooltip>\n            </Button>\n            <Button text=\"%Button.Export\" onAction=\"#handleAccountExport\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"FILE_EXCEL_O\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ExportTransactions\"/>\n                </tooltip>\n            </Button>\n            <Button text=\"%Button.Print\" onAction=\"#handleAccountReport\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"PRINT\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.PrintRegRep\"/>\n                </tooltip>\n            </Button>\n            <Button text=\"%Button.Resize\" fx:id=\"packColumnsButton\" onAction=\"#handleTableColumnPack\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"TABLE_COLUMN_WIDTH\"/>\n                </graphic>\n                <tooltip>\n                    <Tooltip text=\"%ToolTip.ResizeColumns\"/>\n                </tooltip>\n            </Button>\n        </ToolBar>\n    </top>\n    <center>\n        <SplitPane fx:id=\"splitPane\">\n            <TreeView fx:id=\"treeView\" BorderPane.alignment=\"CENTER\" minWidth=\"120\"/>\n            <StackPane fx:id=\"registerPane\"/>\n        </SplitPane>\n    </center>\n</BorderPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/ReinvestDividendSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.FeePane?>\n<?import jgnash.uifx.views.register.GainLossPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.ReinvestDividendSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Price\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <GridPane styleClass=\"form\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" GridPane.fillWidth=\"true\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <DecimalTextField fx:id=\"priceField\" GridPane.columnIndex=\"0\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Quantity\" GridPane.columnIndex=\"1\"/>\n        <DecimalTextField fx:id=\"quantityField\" GridPane.columnIndex=\"2\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Gains\" GridPane.columnIndex=\"3\"/>\n        <GainLossPane fx:id=\"gainLossPane\" GridPane.columnIndex=\"4\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Fees\" GridPane.columnIndex=\"5\"/>\n        <FeePane fx:id=\"feePane\" GridPane.columnIndex=\"6\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n    </GridPane>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Total\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"totalField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"3\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"0\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/ReturnOfCapitalSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.ReturnOfCapitalSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.IncomeAccount\" GridPane.rowIndex=\"2\"/>\n    <AccountExchangePane fx:id=\"incomeExchangePane\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                         GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.ReturnOfCapital\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"decimalTextField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <Label text=\"%Label.Account\" GridPane.rowIndex=\"3\"/>\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"3\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"1\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"3\"/>\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/SellShareSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.FeePane?>\n<?import jgnash.uifx.views.register.GainLossPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.SellShareSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Price\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <GridPane styleClass=\"form\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" GridPane.fillWidth=\"true\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <DecimalTextField fx:id=\"priceField\" GridPane.columnIndex=\"0\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Quantity\" GridPane.columnIndex=\"1\"/>\n        <DecimalTextField fx:id=\"quantityField\" GridPane.columnIndex=\"2\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Gains\" GridPane.columnIndex=\"3\"/>\n        <GainLossPane fx:id=\"gainLossPane\" GridPane.columnIndex=\"4\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n        <Label text=\"%Label.Fees\" GridPane.columnIndex=\"5\"/>\n        <FeePane fx:id=\"feePane\" GridPane.columnIndex=\"6\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\"/>\n    </GridPane>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Total\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"totalField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <Label text=\"%Label.Account\" GridPane.rowIndex=\"3\"/>\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"3\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"1\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"3\"/>\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"5\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/SplitMergeSharesSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.SecurityComboBox?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.SplitMergeSharesSlipController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"95\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <!-- Row 0 -->\n    <Label text=\"%Label.Security\"/>\n    <SecurityComboBox fx:id=\"securityComboBox\" maxWidth=\"Infinity\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <!-- Row 1 -->\n    <Label text=\"%Label.Price\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <GridPane styleClass=\"form\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" GridPane.fillWidth=\"true\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"80\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <DecimalTextField fx:id=\"priceField\" GridPane.columnIndex=\"0\"/>\n        <Label text=\"%Label.Quantity\" GridPane.columnIndex=\"1\"/>\n        <DecimalTextField fx:id=\"quantityField\" GridPane.columnIndex=\"2\"/>\n\n    </GridPane>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <!-- Row 2 -->\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"2\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.fillWidth=\"true\" GridPane.columnIndex=\"1\"\n                           GridPane.rowIndex=\"2\"/>\n    <Label text=\"%Label.Total\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"totalField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n\n    <!-- Row 3 -->\n    <GridPane styleClass=\"form\" GridPane.halignment=\"LEFT\" GridPane.vgrow=\"NEVER\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\" GridPane.columnSpan=\"4\" GridPane.rowIndex=\"3\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\" GridPane.columnIndex=\"0\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/SplitTransactionDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.VBox?>\n<?import javafx.scene.Scene?>\n<?import javafx.stage.Stage?>\n<?import jgnash.uifx.control.TableViewEx?>\n\n<fx:root type=\"javafx.stage.Stage\" xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n         minWidth=\"680\" minHeight=\"440\">\n    <scene>\n        <Scene>\n            <BorderPane>\n                <center>\n                    <VBox styleClass=\"dialog, form\">\n                        <TableViewEx fx:id=\"tableView\" VBox.vgrow=\"ALWAYS\"/>\n                        <ButtonBar VBox.vgrow=\"NEVER\">\n                            <buttons>\n                                <Button fx:id=\"newButton\" text=\"%Button.New\" ButtonBar.buttonData=\"LEFT\"/>\n                                <Button fx:id=\"deleteButton\" text=\"%Button.Delete\" ButtonBar.buttonData=\"LEFT\"/>\n                                <Button visible=\"false\" ButtonBar.buttonData=\"BIG_GAP\"/>\n                                <Button fx:id=\"deleteAllButton\" text=\"%Button.DeleteAll\" ButtonBar.buttonData=\"RIGHT\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </VBox>\n                </center>\n                <bottom>\n                    <VBox styleClass=\"dialog, form\">\n                        <CheckBox text=\"%Button.ConcatenateMemos\" fx:id=\"concatenateMemosCheckBox\"/>\n                        <TabPane tabClosingPolicy=\"UNAVAILABLE\" fx:id=\"tabPane\" VBox.vgrow=\"NEVER\" styleClass=\"border\"/>\n                        <ButtonBar VBox.vgrow=\"NEVER\">\n                            <buttons>\n                                <Button fx:id=\"closeButton\" text=\"%Button.Close\" ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n                            </buttons>\n                        </ButtonBar>\n                    </VBox>\n                </bottom>\n            </BorderPane>\n        </Scene>\n    </scene>\n</fx:root>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/SplitTransactionSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.SplitTransactionSlipController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"95\" maxWidth=\"95\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.Account\"/>\n    <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.columnIndex=\"1\" GridPane.hgrow=\"ALWAYS\"\n                         GridPane.fillWidth=\"true\"/>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"3\"/>\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoField\" GridPane.columnIndex=\"1\" GridPane.columnSpan=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <GridPane GridPane.rowIndex=\"2\" GridPane.columnSpan=\"4\" GridPane.fillWidth=\"true\" GridPane.vgrow=\"NEVER\"\n              styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints hgrow=\"NEVER\" minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"2\"/>\n        <ButtonBar GridPane.columnIndex=\"3\">\n            <buttons>\n                <Button text=\"%Button.Enter\" fx:id=\"enterButton\" ButtonBar.buttonData=\"OK_DONE\" onAction=\"#handleEnterAction\"/>\n                <Button text=\"%Button.Clear\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#handleCancelAction\"/>\n            </buttons>\n        </ButtonBar>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/TransactionDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.TabPane?>\n<?import javafx.scene.layout.BorderPane?>\n\n<?import javafx.scene.Scene?>\n\n<fx:root type=\"javafx.stage.Stage\" xmlns=\"http://javafx.com/javafx/8\" xmlns:fx=\"http://javafx.com/fxml/1\" minWidth=\"680\" minHeight=\"150\">\n    <scene>\n        <Scene>\n            <BorderPane styleClass=\"dialog, form\">\n                <center>\n                    <TabPane tabClosingPolicy=\"UNAVAILABLE\" fx:id=\"tabPane\"/>\n                </center>\n            </BorderPane>\n        </Scene>\n    </scene>\n</fx:root>\n\n\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/TransactionTagDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.ScrollPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.TilePane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.TransactionTagDialogController\" prefHeight=\"300.0\" prefWidth=\"400.0\"\n          styleClass=\"form, dialog\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"ALWAYS\" valignment=\"TOP\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <ButtonBar>\n        <buttons>\n            <Button onAction=\"#handleClearAllAction\" text=\"%Button.ClearAll\"\n                    ButtonBar.buttonData=\"LEFT\"\n                    ButtonBar.buttonUniformSize=\"true\"/>\n            <Button ButtonBar.buttonData=\"SMALL_GAP\" visible=\"false\"/>\n        </buttons>\n    </ButtonBar>\n\n    <ScrollPane fitToWidth=\"true\" hbarPolicy=\"NEVER\" GridPane.rowIndex=\"1\">\n        <TilePane fx:id=\"tilePane\"/>\n    </ScrollPane>\n\n    <ButtonBar GridPane.rowIndex=\"2\">\n        <buttons>\n            <Button mnemonicParsing=\"false\" text=\"%Button.Ok\" ButtonBar.buttonData=\"OK_DONE\"\n                    onAction=\"#handleOkAction\"/>\n            <Button mnemonicParsing=\"false\" onAction=\"#handleCloseAction\" text=\"%Button.Cancel\"\n                    ButtonBar.buttonData=\"CANCEL_CLOSE\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/TransactionTagPane.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.HBox?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<fx:root type=\"GridPane\" maxHeight=\"-Infinity\" maxWidth=\"Infinity\" minHeight=\"-Infinity\" minWidth=\"-Infinity\"\n         xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <Button fx:id=\"selectTagsButton\" visible=\"false\" mnemonicParsing=\"false\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"TAGS\"/>\n        </graphic>\n        <tooltip>\n            <Tooltip text=\"%ToolTip.SelectTags\"/>\n        </tooltip>\n    </Button>\n\n    <HBox fx:id=\"tagBox\" GridPane.columnIndex=\"1\" alignment=\"CENTER\"/>\n\n</fx:root>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/TransferSlip.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AutoCompleteTextField?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n<?import jgnash.uifx.control.TransactionNumberComboBox?>\n<?import jgnash.uifx.views.register.AccountExchangePane?>\n<?import jgnash.uifx.views.register.AttachmentPane?>\n<?import jgnash.uifx.views.register.TransactionTagPane?>\n\n<GridPane xmlns=\"http://javafx.com/javafx\" xmlns:fx=\"http://javafx.com/fxml\"\n          fx:controller=\"jgnash.uifx.views.register.TransferSlipController\" styleClass=\"form, dialog\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"-Infinity\" fx:id=\"dateColumnConstraint\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.TransferTo\" GridPane.rowIndex=\"0\"/>\n    <AccountExchangePane fx:id=\"accountExchangePane\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.Number\" GridPane.columnIndex=\"2\"/>\n    <TransactionNumberComboBox fx:id=\"numberComboBox\" editable=\"true\" GridPane.columnIndex=\"3\"/>\n\n    <Label text=\"%Label.Memo\" GridPane.rowIndex=\"1\"/>\n    <AutoCompleteTextField fx:id=\"memoTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.Date\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"1\"/>\n    <DatePickerEx fx:id=\"datePicker\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"1\"/>\n\n    <GridPane GridPane.columnSpan=\"2\" GridPane.halignment=\"LEFT\" GridPane.hgrow=\"NEVER\" GridPane.rowIndex=\"2\"\n              GridPane.vgrow=\"NEVER\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n            <ColumnConstraints minWidth=\"8\" maxWidth=\"8\"/>\n            <ColumnConstraints hgrow=\"NEVER\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n\n        <CheckBox fx:id=\"reconciledButton\" mnemonicParsing=\"false\" text=\"%Button.Cleared\"/>\n        <AttachmentPane fx:id=\"attachmentPane\" GridPane.columnIndex=\"2\"/>\n        <TransactionTagPane fx:id=\"tagPane\" GridPane.columnIndex=\"4\"/>\n    </GridPane>\n    <Label text=\"%Label.Amount\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"amountField\" GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\"/>\n    <ButtonBar fx:id=\"buttonBar\" GridPane.columnIndex=\"0\" GridPane.columnSpan=\"4\" GridPane.rowIndex=\"3\">\n        <buttons>\n            <Button fx:id=\"enterButton\" text=\"%Button.Enter\" ButtonBar.buttonData=\"OK_DONE\"\n                    onAction=\"#handleEnterAction\"/>\n            <Button text=\"%Button.Cancel\" ButtonBar.buttonData=\"CANCEL_CLOSE\" onAction=\"#handleCancelAction\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/reconcile/ReconcileDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.Separator?>\n<?import javafx.scene.control.TableView?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.StackPane?>\n<?import jgnash.uifx.control.TableViewEx?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" minHeight=\"-Infinity\" minWidth=\"600\"\n          fx:controller=\"jgnash.uifx.views.register.reconcile.ReconcileDialogController\" styleClass=\"dialog, form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\" percentWidth=\"50.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" percentWidth=\"50.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"ALWAYS\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <TitledPane fx:id=\"decreaseTitledPane\" collapsible=\"false\" maxHeight=\"Infinity\" text=\"Decrease\"\n                GridPane.rowSpan=\"2\">\n        <BorderPane styleClass=\"form\">\n            <center>\n                <TableViewEx fx:id=\"decreaseTableView\" prefHeight=\"200.0\" minWidth=\"300.0\"/>\n            </center>\n            <bottom>\n                <GridPane  styleClass=\"form\">\n                    <columnConstraints>\n                        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"300.0\"/>\n                    </columnConstraints>\n                    <rowConstraints>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                    </rowConstraints>\n\n                    <StackPane GridPane.rowIndex=\"0\" styleClass=\"summary-bar\">\n                        <Label fx:id=\"decreaseTotalLabel\" text=\"0.00\" StackPane.alignment=\"CENTER_RIGHT\"/>\n                    </StackPane>\n                    <ButtonBar GridPane.rowIndex=\"1\">\n                        <buttons>\n                            <Button text=\"%Button.SelectAll\" onAction=\"#handleDecreaseSelectAllAction\"/>\n                            <Button text=\"%Button.ClearAll\" onAction=\"#handleDecreaseClearAllAction\"/>\n                        </buttons>\n                    </ButtonBar>\n                </GridPane>\n            </bottom>\n        </BorderPane>\n    </TitledPane>\n    <TitledPane fx:id=\"increaseTitledPane\" collapsible=\"false\" maxHeight=\"Infinity\" text=\"Increase\"\n                GridPane.columnIndex=\"1\">\n        <BorderPane styleClass=\"form\">\n            <center>\n                <TableView fx:id=\"increaseTableView\" prefHeight=\"200.0\" minWidth=\"300.0\"/>\n            </center>\n            <bottom>\n                <GridPane  styleClass=\"form\">\n                    <columnConstraints>\n                        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"300.0\"/>\n                    </columnConstraints>\n                    <rowConstraints>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                        <RowConstraints vgrow=\"NEVER\"/>\n                    </rowConstraints>\n\n                    <StackPane GridPane.rowIndex=\"0\" styleClass=\"summary-bar\">\n                        <Label fx:id=\"increaseTotalLabel\" text=\"0.00\" StackPane.alignment=\"CENTER_RIGHT\"/>\n                    </StackPane>\n                    <ButtonBar GridPane.rowIndex=\"1\">\n                        <buttons>\n                            <Button text=\"%Button.SelectAll\" onAction=\"#handleIncreaseSelectAllAction\"/>\n                            <Button text=\"%Button.ClearAll\" onAction=\"#handleIncreaseClearAllAction\"/>\n                        </buttons>\n                    </ButtonBar>\n                </GridPane>\n            </bottom>\n        </BorderPane>\n    </TitledPane>\n    <GridPane GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n            <RowConstraints vgrow=\"NEVER\"/>\n        </rowConstraints>\n        <Label text=\"%Label.OpeningBalance\"/>\n        <Label fx:id=\"openingBalanceLabel\" text=\"0.00\" textAlignment=\"RIGHT\" GridPane.columnIndex=\"1\"\n               GridPane.halignment=\"RIGHT\"/>\n        <Label text=\"%Label.TargetBalance\" GridPane.rowIndex=\"1\"/>\n        <Label fx:id=\"targetBalanceLabel\" text=\"0.00\" GridPane.columnIndex=\"1\" GridPane.halignment=\"RIGHT\"\n               GridPane.rowIndex=\"1\"/>\n        <Label text=\"%Label.ReconciledBalance\" GridPane.rowIndex=\"2\"/>\n        <Label fx:id=\"reconciledBalanceLabel\" text=\"0.00\" GridPane.columnIndex=\"1\" GridPane.halignment=\"RIGHT\"\n               GridPane.rowIndex=\"2\"/>\n        <Separator GridPane.columnSpan=\"2\" GridPane.rowIndex=\"3\"/>\n        <Label text=\"%Label.Difference\" GridPane.rowIndex=\"4\"/>\n        <Label fx:id=\"differenceLabel\" text=\"0.00\" GridPane.columnIndex=\"1\" GridPane.halignment=\"RIGHT\" GridPane.rowIndex=\"4\"/>\n    </GridPane>\n    <ButtonBar GridPane.columnSpan=\"2\" GridPane.rowIndex=\"2\">\n        <buttons>\n            <Button text=\"%Button.Cancel\" onAction=\"#handleCloseAction\" ButtonBar.buttonData=\"RIGHT\"/>\n            <Button text=\"%Button.FinishLater\" onAction=\"#handleFinishLaterAction\" ButtonBar.buttonData=\"RIGHT\"/>\n            <Button fx:id=\"finishButton\" text=\"%Button.Finish\" onAction=\"#handleFinishAction\" ButtonBar.buttonData=\"RIGHT\"/>\n        </buttons>\n    </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/views/register/reconcile/ReconcileSettingsDialog.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.CheckBox?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DatePickerEx?>\n<?import jgnash.uifx.control.DecimalTextField?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\"\n          fx:controller=\"jgnash.uifx.views.register.reconcile.ReconcileSettingsDialogController\"\n          minHeight=\"-Infinity\" minWidth=\"-Infinity\" styleClass=\"dialog, form\">\n\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\"/>\n        <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"110.0\" prefWidth=\"110.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n        <RowConstraints vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.StatementDate\"/>\n    <DatePickerEx fx:id=\"datePicker\" maxWidth=\"Infinity\" onAction=\"#handleUpdateBalance\" GridPane.columnIndex=\"1\"/>\n    <Label text=\"%Label.OpeningBalance\" GridPane.rowIndex=\"1\"/>\n    <DecimalTextField fx:id=\"openingBalanceTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"/>\n    <Label text=\"%Label.EndingBalance\" GridPane.rowIndex=\"2\"/>\n    <DecimalTextField fx:id=\"closingBalanceTextField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\"/>\n    <CheckBox fx:id=\"autoFillBalanceCheckBox\" text=\"%Button.CalcBal\" onAction=\"#handleUpdateBalance\" GridPane.columnSpan=\"2\" GridPane.rowIndex=\"3\"/>\n    <ButtonBar fx:id=\"buttonBar\" GridPane.columnSpan=\"2\" GridPane.rowIndex=\"4\">\n        <buttons>\n            <Button text=\"%Button.Ok\" onAction=\"#handleOkayAction\"/>\n            <Button text=\"%Button.Cancel\" onAction=\"#handleCloseAction\"/>\n        </buttons>\n    </ButtonBar>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/file/NewFileFour.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.control.TreeView?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<?import jgnash.uifx.control.CheckListView?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8.0.45\" maxHeight=\"Infinity\"\n          maxWidth=\"Infinity\"\n          fx:controller=\"jgnash.uifx.wizard.file.NewFileFourController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"ALWAYS\"/>\n    </rowConstraints>\n    <TextArea fx:id=\"textArea\" editable=\"false\" wrapText=\"true\" prefRowCount=\"7\" GridPane.fillHeight=\"true\"\n              GridPane.columnSpan=\"2\"/>\n    <CheckListView fx:id=\"accountSetsList\" GridPane.fillHeight=\"true\" GridPane.rowIndex=\"1\"/>\n    <TreeView fx:id=\"accountTreeView\" GridPane.fillHeight=\"true\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/file/NewFileOne.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.control.Tooltip?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.DataStoreTypeComboBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" maxHeight=\"Infinity\" maxWidth=\"Infinity\"\n          fx:controller=\"jgnash.uifx.wizard.file.NewFileOneController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"NEVER\" />\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"ALWAYS\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <TextArea fx:id=\"textArea\" editable=\"false\" wrapText=\"true\" GridPane.columnSpan=\"4\" GridPane.fillHeight=\"true\"\n              GridPane.vgrow=\"ALWAYS\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.StorageType\" GridPane.rowIndex=\"1\"/>\n    <DataStoreTypeComboBox fx:id=\"storageTypeComboBox\" onAction=\"#handleDataStoreTypeAction\" prefWidth=\"150.0\"\n                           GridPane.columnIndex=\"1\" GridPane.columnSpan=\"3\"\n                           maxWidth=\"Infinity\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\" GridPane.rowIndex=\"1\"/>\n\n    <Label text=\"%Label.DatabaseName\" GridPane.rowIndex=\"2\"/>\n    <TextField fx:id=\"fileNameField\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\" minWidth=\"75\"/>\n    <Button onAction=\"#handleFileButtonAction\" mnemonicParsing=\"false\" GridPane.columnIndex=\"2\" GridPane.rowIndex=\"2\">\n        <graphic>\n            <MaterialDesignLabel glyphName=\"ELLIPSIS_H\"/>\n        </graphic>\n    </Button>\n    <MaterialDesignLabel fx:id=\"warningLabel\" glyphName=\"EXCLAMATION_TRIANGLE\" size=\"24\"\n                      GridPane.columnIndex=\"3\" GridPane.rowIndex=\"2\">\n        <tooltip>\n            <Tooltip text=\"%Message.OverwriteDB\"/>\n        </tooltip>\n    </MaterialDesignLabel>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/file/NewFileSummary.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TextField?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8.0.45\" maxHeight=\"Infinity\"\n          maxWidth=\"Infinity\"\n          fx:controller=\"jgnash.uifx.wizard.file.NewFileSummaryController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" maxWidth=\"Infinity\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"SOMETIMES\"/>\n    </rowConstraints>\n    <Label text=\"%Label.FileName\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"0\"/>\n    <TextField fx:id=\"fileNameField\" editable=\"false\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"0\"\n               maxWidth=\"Infinity\"/>\n    <Label text=\"%Label.DefaultCurrency\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <TextField fx:id=\"defaultCurrencyField\" editable=\"false\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\"\n               maxWidth=\"Infinity\"/>\n    <Label text=\"%Label.Currencies\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"2\" GridPane.valignment=\"TOP\"/>\n    <ListView fx:id=\"currenciesList\" GridPane.columnIndex=\"1\" GridPane.rowIndex=\"2\" prefHeight=\"100\"/>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/file/NewFileThree.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ListView?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.control.TitledPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.layout.VBox?>\n<?import jgnash.uifx.resource.font.MaterialDesignLabel?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" maxWidth=\"Infinity\"\n          fx:controller=\"jgnash.uifx.wizard.file.NewFileThreeController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" vgrow=\"ALWAYS\"/>\n    </rowConstraints>\n    <TextArea fx:id=\"textArea\" editable=\"false\" wrapText=\"true\" GridPane.fillHeight=\"true\" prefRowCount=\"7\"/>\n    <GridPane GridPane.rowIndex=\"1\" styleClass=\"form\">\n        <columnConstraints>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n            <ColumnConstraints hgrow=\"SOMETIMES\" minWidth=\"80.0\"/>\n            <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\"/>\n        </columnConstraints>\n        <rowConstraints>\n            <RowConstraints minHeight=\"100\" vgrow=\"ALWAYS\"/>\n        </rowConstraints>\n        <TitledPane text=\"%Title.Available\" collapsible=\"false\" prefHeight=\"100\" maxHeight=\"Infinity\"\n                    GridPane.fillHeight=\"true\">\n            <ListView fx:id=\"availableList\"/>\n        </TitledPane>\n        <VBox GridPane.columnIndex=\"1\" styleClass=\"form\" GridPane.fillHeight=\"true\">\n            <Button text=\"%Button.Add\" onAction=\"#handleAddAction\" contentDisplay=\"RIGHT\" maxWidth=\"Infinity\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHEVRON_RIGHT\"/>\n                </graphic>\n            </Button>\n            <Button text=\"%Button.Remove\" onAction=\"#handleRemoveAction\" maxWidth=\"Infinity\">\n                <graphic>\n                    <MaterialDesignLabel glyphName=\"CHEVRON_LEFT\"/>\n                </graphic>\n            </Button>\n        </VBox>\n        <TitledPane text=\"%Title.Selected\" collapsible=\"false\" prefHeight=\"100\" maxHeight=\"Infinity\"\n                    GridPane.fillHeight=\"true\" GridPane.columnIndex=\"2\">\n            <ListView fx:id=\"selectedList\"/>\n        </TitledPane>\n    </GridPane>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/file/NewFileTwo.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.ComboBox?>\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.control.TextArea?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8.0.45\" maxHeight=\"Infinity\"\n          maxWidth=\"Infinity\"\n          fx:controller=\"jgnash.uifx.wizard.file.NewFileTwoController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"ALWAYS\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <TextArea fx:id=\"textArea\" editable=\"false\" wrapText=\"true\" GridPane.columnSpan=\"2\" GridPane.fillHeight=\"true\"\n              GridPane.vgrow=\"ALWAYS\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.DefaultCurrency\" GridPane.columnIndex=\"0\" GridPane.rowIndex=\"1\"/>\n    <ComboBox fx:id=\"defaultCurrencyComboBox\" prefWidth=\"150.0\"\n              GridPane.columnIndex=\"1\" GridPane.rowIndex=\"1\" maxWidth=\"Infinity\" GridPane.hgrow=\"ALWAYS\"\n              GridPane.fillWidth=\"true\"/>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/imports/ImportPageOne.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import jgnash.uifx.control.AccountComboBox?>\n\n<?import javafx.scene.text.TextFlow?>\n<?import javafx.scene.control.ChoiceBox?>\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\" maxHeight=\"Infinity\"\n          maxWidth=\"Infinity\" fx:controller=\"jgnash.uifx.wizard.imports.ImportPageOneController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n    </rowConstraints>\n    <TextFlow fx:id=\"textFlow\"  GridPane.columnSpan=\"2\" GridPane.fillHeight=\"true\"\n              GridPane.vgrow=\"ALWAYS\" maxWidth=\"Infinity\"/>\n\n    <Label text=\"%Label.DestAccount\" GridPane.rowIndex=\"1\"/>\n    <AccountComboBox fx:id=\"accountComboBox\" prefWidth=\"150.0\" GridPane.columnIndex=\"1\"\n                     maxWidth=\"Infinity\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\" GridPane.rowIndex=\"1\"/>\n\n    <Label text=\"%Label.DateFormat\" fx:id=\"dateFormatLabel\" GridPane.rowIndex=\"2\"/>\n    <ChoiceBox disable=\"true\" fx:id=\"dateFormatChoiceBox\" prefWidth=\"150.0\" GridPane.rowIndex=\"2\" GridPane.columnIndex=\"1\"\n               maxWidth=\"Infinity\" GridPane.hgrow=\"ALWAYS\" GridPane.fillWidth=\"true\"/>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/imports/ImportPageThree.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.scene.control.Label?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml/1\" xmlns=\"http://javafx.com/javafx/8\" maxHeight=\"Infinity\"\n          maxWidth=\"Infinity\" fx:controller=\"jgnash.uifx.wizard.imports.ImportPageThreeController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"NEVER\" minWidth=\"10.0\"/>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints  vgrow=\"NEVER\"/>\n        <RowConstraints  vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <Label text=\"%Label.DestAccount\" />\n    <Label fx:id=\"destLabel\" GridPane.columnIndex=\"1\"/>\n\n    <Label text=\"%Label.NumTrans\" GridPane.rowIndex=\"1\"/>\n    <Label fx:id=\"transCountLabel\" GridPane.rowIndex=\"1\" GridPane.columnIndex=\"1\"/>\n\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/main/resources/jgnash/uifx/wizard/imports/ImportPageTwo.fxml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<?import javafx.geometry.Insets?>\n<?import javafx.scene.control.Button?>\n<?import javafx.scene.control.ButtonBar?>\n<?import javafx.scene.layout.BorderPane?>\n<?import javafx.scene.layout.ColumnConstraints?>\n<?import javafx.scene.layout.GridPane?>\n<?import javafx.scene.layout.RowConstraints?>\n<?import javafx.scene.text.TextFlow?>\n<?import jgnash.uifx.control.TableViewEx?>\n\n<GridPane xmlns:fx=\"http://javafx.com/fxml\" xmlns=\"http://javafx.com/javafx\" maxHeight=\"Infinity\"\n          maxWidth=\"Infinity\" fx:controller=\"jgnash.uifx.wizard.imports.ImportPageTwoController\" styleClass=\"form\">\n    <columnConstraints>\n        <ColumnConstraints hgrow=\"ALWAYS\" minWidth=\"10.0\" prefWidth=\"100.0\"/>\n    </columnConstraints>\n    <rowConstraints>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"ALWAYS\"/>\n        <RowConstraints minHeight=\"10.0\" prefHeight=\"30.0\" vgrow=\"NEVER\"/>\n    </rowConstraints>\n\n    <BorderPane GridPane.rowIndex=\"0\" styleClass=\"form\">\n        <top>\n            <TextFlow fx:id=\"textFlow\">\n                <BorderPane.margin>\n                    <Insets bottom=\"10.0\"/>\n                </BorderPane.margin>\n            </TextFlow>\n        </top>\n        <center>\n            <TableViewEx fx:id=\"tableView\"/>\n        </center>\n    </BorderPane>\n\n   <ButtonBar GridPane.rowIndex=\"1\" maxWidth=\"Infinity\">\n       <buttons>\n           <Button text=\"%Button.Delete\" fx:id=\"deleteButton\" onAction=\"#handleDeleteAction\" ButtonBar.buttonData=\"LEFT\"/>\n           <Button visible=\"false\" ButtonBar.buttonData=\"BIG_GAP\"/>\n       </buttons>\n   </ButtonBar>\n</GridPane>\n"
  },
  {
    "path": "jgnash-fx/src/test/java/jgnash/uifx/ControlsTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.util.Locale;\nimport java.util.Objects;\n\nimport javafx.application.Application;\nimport javafx.beans.property.ObjectProperty;\nimport javafx.beans.property.SimpleObjectProperty;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.DatePicker;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.DefaultCurrencies;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.SecurityNode;\nimport jgnash.uifx.control.AccountComboBox;\nimport jgnash.uifx.control.DatePickerEx;\nimport jgnash.uifx.control.DecimalTextField;\nimport jgnash.uifx.control.DetailedDecimalTextField;\nimport jgnash.uifx.control.PopOverButton;\nimport jgnash.uifx.control.SecurityComboBox;\nimport jgnash.uifx.control.TextFieldEx;\nimport jgnash.uifx.control.TimePeriodComboBox;\nimport jgnash.uifx.control.TransactionNumberComboBox;\nimport jgnash.uifx.resource.font.MaterialDesignLabel;\nimport jgnash.uifx.skin.ThemeManager;\nimport jgnash.util.LogUtil;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * UI controls test application.\n *\n * @author Craig Cavanaugh\n */\npublic class ControlsTest extends Application {\n\n    private String testFile;\n\n    private String tempFile;\n\n    public static void main(final String[] args) {\n        //Locale.setDefault(Locale.FRANCE);\n\n        launch(args);\n    }\n   \n\t@Override\n    public void start(final Stage primaryStage) {\n\n        Engine engine = createEngine();\n        Objects.requireNonNull(engine);\n\n        ThemeManager.restoreLastUsedTheme();\n\n        DecimalTextField decimalTextField = new DecimalTextField();\n        DecimalTextField decimalTextField2 = new DecimalTextField();\n        decimalTextField2.scaleProperty().set(4);\n\n        primaryStage.setTitle(\"Controls Test\");\n        Button btn = new Button();\n\n        btn.setText(\"getDecimal()\");\n\n        // Create the DatePicker.\n        DatePicker datePicker = new DatePickerEx();\n\n        datePicker.setOnAction(event -> {\n            LocalDate date = datePicker.getValue();\n            System.out.println(\"Selected date: \" + date);\n        });\n\n        decimalTextField.decimalProperty().addListener((observable, oldValue, newValue)\n                -> System.out.println(\"decimalTextField: \" + newValue));\n\n        decimalTextField2.decimalProperty().addListener((observable, oldValue, newValue)\n                -> System.out.println(\"decimalTextField2: \" + newValue));\n\n        ObjectProperty<BigDecimal> decimal = new SimpleObjectProperty<>();\n\n        decimalTextField2.decimalProperty().bindBidirectional(decimal);\n        decimal.set(BigDecimal.TEN);\n\n        btn.setOnAction(event -> {\n            decimal.set(BigDecimal.ONE);\n            System.out.println(decimalTextField2.getDecimal());\n        });\n\n        System.out.println(decimal.isBound());\n        System.out.println(decimalTextField2.decimalProperty().isBound());\n\n        TransactionNumberComboBox numberComboBox = new TransactionNumberComboBox();\n        numberComboBox.accountProperty().set(engine.getAccountList().get(0));\n\n        Button exceptionButton = new Button(\"Show Exception\");\n        exceptionButton.setOnAction(event -> StaticUIMethods.displayException(new Exception(\"Test exception\")));\n\n        SecurityComboBox securityComboBox = new SecurityComboBox();\n\n        TextFieldEx textFieldEx = new TextFieldEx();\n\n        PopOverButton popOverButton = new PopOverButton(new MaterialDesignLabel(MaterialDesignLabel.MDIcon.SWAP_HORIZONTAL));\n        popOverButton.setContentNode(new DecimalTextField());\n\n        DetailedDecimalTextField detailedDecimalTextField2 = new DetailedDecimalTextField();\n\n        VBox vBox = new VBox();\n        vBox.getChildren().addAll(decimalTextField, decimalTextField2, datePicker, new AccountComboBox(),\n                numberComboBox, btn, exceptionButton, securityComboBox, new TimePeriodComboBox(), textFieldEx,\n                popOverButton, detailedDecimalTextField2);\n\n        primaryStage.setScene(new Scene(vBox, 300, 420));\n\n        ThemeManager.applyStyleSheets(primaryStage.getScene());\n        primaryStage.getScene().getRoot().getStyleClass().addAll(\"form\", \"dialog\");\n\n        primaryStage.show();\n        primaryStage.requestFocus();\n    }\n\n    @Override\n    public void stop() throws IOException {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        Files.deleteIfExists(Paths.get(testFile));\n\n        cleanup();\n    }\n\n    private Engine createEngine() {\n        try {\n            testFile = Files.createTempFile(\"test\",\n                    DataStoreType.BINARY_XSTREAM.getDataStore().getFileExt()).toFile().getAbsolutePath();\n            tempFile = testFile;\n        } catch (IOException e1) {\n            LogUtil.logSevere(ControlsTest.class, e1);\n        }\n\n        EngineFactory.deleteDatabase(testFile);\n\n        final Engine engine = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT,\n                EngineFactory.EMPTY_PASSWORD, DataStoreType.BINARY_XSTREAM);\n\n        Objects.requireNonNull(engine);\n\n        CurrencyNode node = engine.getDefaultCurrency();\n\n        if (!node.getSymbol().equals(\"USD\")) {\n            engine.setDefaultCurrency(DefaultCurrencies.buildNode(Locale.US));\n        }\n\n        node = engine.getCurrency(\"CAD\");\n\n        if (node == null) {\n            node = DefaultCurrencies.buildNode(Locale.CANADA);\n            assertNotNull(node);\n            assertTrue(engine.addCurrency(node));\n        }\n\n        Account account = new Account(AccountType.BANK, engine.getDefaultCurrency());\n        account.setName(\"Bank Accounts\");\n\n        engine.addAccount(engine.getRootAccount(), account);\n\n        SecurityNode securityNode = new SecurityNode();\n        securityNode.setSymbol(\"GGG\");\n        securityNode.setDescription(\"Google\");\n        securityNode.setReportedCurrencyNode(engine.getDefaultCurrency());\n\n        engine.addSecurity(securityNode);\n\n        securityNode = new SecurityNode();\n        securityNode.setSymbol(\"MSFT\");\n        securityNode.setDescription(\"Microsoft\");\n        securityNode.setReportedCurrencyNode(engine.getDefaultCurrency());\n\n        engine.addSecurity(securityNode);\n\n        return engine;\n    }\n\n    private void cleanup() throws IOException {\n        Files.deleteIfExists(Paths.get(tempFile));\n        Files.deleteIfExists(Paths.get(tempFile + \".backup\"));\n    }\n}\n"
  },
  {
    "path": "jgnash-fx/src/test/java/jgnash/uifx/control/autocomplete/AutoCompleteModelTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.uifx.control.autocomplete;\n\nimport java.io.File;\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineException;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.xstream.BinaryXStreamDataStore;\nimport jgnash.uifx.Options;\nimport jgnash.uifx.control.AutoCompleteTextField;\n\nimport org.awaitility.Awaitility;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledOnOs;\nimport org.junit.jupiter.api.condition.OS;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.testfx.api.FxToolkit.registerPrimaryStage;\n\n/**\n * Unit test for the Autocomplete models\n */\nclass AutoCompleteModelTest {\n    private static final int TRANSACTION_COUNT = 2000;\n\n    @SuppressWarnings({\"WeakerAccess\", \"unused\"})\n    @TempDir\n    static Path tempDir;\n\n    @BeforeAll\n    static void setUp() throws TimeoutException {\n        assertTrue(Files.isDirectory(tempDir));\n\n        // setup for a headless env\n        System.setProperty(\"java.awt.headless\", \"true\");\n        System.setProperty(\"testfx.robot\", \"glass\");\n        System.setProperty(\"testfx.headless\", \"true\");\n        System.setProperty(\"prism.order\", \"sw\");\n        System.setProperty(\"prism.text\", \"t2k\");\n        System.setProperty(\"prism.verbose\", \"true\");\n\n\n        registerPrimaryStage();\n    }\n\n    @SuppressWarnings(\"SameParameterValue\")\n    Engine createEngine(final String fileName) {\n\n        final String database = tempDir.toString() + File.separator + fileName + BinaryXStreamDataStore.FILE_EXT;\n\n        System.out.println(database);\n\n        try {\n            return EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.BINARY_XSTREAM);\n        } catch (final EngineException e) {\n            fail(\"Fatal error occurred\");\n            return null;\n        }\n    }\n\n    @Test\n    @DisabledOnOs(OS.MAC)\n    void testPayeeMemoModels() {\n        Options.useFuzzyMatchForAutoCompleteProperty().set(true);\n\n        final Engine engine = createEngine(\"payee-test\");\n\n        final Account account = new Account(AccountType.CASH, engine.getDefaultCurrency());\n\n        engine.addAccount(engine.getRootAccount(), account);\n        assertEquals(1, engine.getAccountList().size());\n\n        final LocalDate localDate = LocalDate.of(2000, Month.JANUARY, 1);\n\n        // create a bunch of transactions\n        for (int i = 1; i <= TRANSACTION_COUNT; i++) {\n            final Transaction t = TransactionFactory.generateSingleEntryTransaction(account, BigDecimal.TEN,\n                    localDate.plusDays(i), \"Memo \" + i, \"Payee \" + i, Integer.toString(i));\n\n            engine.addTransaction(t);\n            assertEquals(i, engine.getTransactions().size());\n        }\n        assertEquals(TRANSACTION_COUNT, engine.getTransactions().size());\n\n        final AutoCompleteTextField<Transaction> autoCompletePayeeTextField = new AutoCompleteTextField<>();\n        AutoCompleteFactory.setPayeeModel(autoCompletePayeeTextField, account);\n\n        final AutoCompleteTextField<Transaction> autoCompleteMemoTextField = new AutoCompleteTextField<>();\n        AutoCompleteFactory.setMemoModel(autoCompleteMemoTextField);\n\n        final AutoCompleteModel<Transaction> payeeModel = autoCompletePayeeTextField.autoCompleteModelObjectProperty().get();\n        final AutoCompleteModel<Transaction> memoModel = autoCompleteMemoTextField.autoCompleteModelObjectProperty().get();\n\n        final AtomicBoolean payeeModelLoaded = payeeModel.isLoadComplete();\n        final AtomicBoolean memoModelLoaded = memoModel.isLoadComplete();\n\n        // Block until the atomics have been set, or the test will fail\n        Awaitility.await().untilTrue(payeeModelLoaded);\n        Awaitility.await().untilTrue(memoModelLoaded);\n\n        //noinspection SpellCheckingInspection\n        final String payeeResult = payeeModel.doLookAhead(\"Paye\");\n        final String memoResult = memoModel.doLookAhead(\"Me\");\n\n        assertEquals(\"Payee \" + TRANSACTION_COUNT, payeeResult);\n        assertEquals(\"Memo \" + TRANSACTION_COUNT, memoResult);\n\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n    }\n}\n"
  },
  {
    "path": "jgnash-fx-test-plugin/build.gradle.kts",
    "content": "description = \"jGnash Test Plugin\"\n\nval javaFXVersion: String by project    // extract JavaFX version from gradle.properties\n\nplugins {\n  id(\"org.openjfx.javafxplugin\")\n}\n\ndependencies {\n  implementation(project(\":jgnash-core\"))\n  implementation(project(\":jgnash-plugin\"))\n  implementation(project(\":jgnash-fx\"))\n}\n\njavafx {\n  version = javaFXVersion\n  modules(\"javafx.controls\")\n}\n\ntasks.jar {\n  // Keep jar clean:\n  exclude (\"META-INF/*.SF\", \"META-INF/*.DSA\", \"META-INF/*.RSA\", \"META-INF/*.MF\")\n\n  // required by the plugin interface\n  manifest {\n    attributes(mapOf(\"Plugin-Activator\" to \"jgnash.uifx.plugin.TestFxPlugin\", \"Plugin-Version\" to \"2.25\"))\n  }\n}\n"
  },
  {
    "path": "jgnash-fx-test-plugin/src/main/java/jgnash/uifx/plugin/TestFxPlugin.java",
    "content": "package jgnash.uifx.plugin;\n\nimport javafx.scene.Node;\nimport javafx.scene.control.MenuBar;\nimport javafx.scene.control.MenuItem;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\n\nimport jgnash.plugin.FxPlugin;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\n\n/**\n * Test plugin.\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"unused\")\npublic class TestFxPlugin implements FxPlugin {\n\n    @Override\n    public Node getOptionsNode() {\n        return new Rectangle(100, 100, Color.LIGHTSTEELBLUE);\n    }\n\n    @Override\n    public String getName() {\n        return \"Test Plugin\";\n    }\n\n    @Override\n    public void start(final PluginPlatform pluginPlatform) {\n        System.out.println(\"Starting test plugin\");\n\n        if (pluginPlatform != PluginPlatform.Fx) {\n            throw new RuntimeException(\"Invalid platform\");\n        }\n\n        JavaFXUtils.runLater(() -> {\n\n            //for API test.  Lookup allows plugins to find nodes within the application scene\n            final Node node = MainView.getInstance().lookup(\"#fileMenu\");\n            if (node != null) {\n                System.out.println(\"found the file menu\");\n                System.out.println(node.getClass().toString()); // Not really a node, but the skin for the node,\n            }\n\n            assert MainView.getInstance().lookup(\"#importMenu\") != null;\n\n            // Install a menu item\n            final MenuBar menuBar = MainView.getInstance().getMenuBar();\n\n            menuBar.getMenus().stream().filter(menu -> menu.getId().equals(\"fileMenu\")).forEach(menu -> {\n                System.out.println(\"found the file menu\");\n                menu.getItems().add(new MenuItem(\"Plugin Menu\"));\n            });\n        });\n\n    }\n\n    @Override\n    public void stop() {\n        System.out.println(\"Stopping test plugin\");\n    }\n}\n"
  },
  {
    "path": "jgnash-manual/readme.md",
    "content": "# Manual Generation\n\nThe jGnash Manual is written using Latex.\n\n## Build Requirements\n* A LaTeX installation with the necessary packages\n    * A Tex Live installation works well.\n* pdfTex _(pdflatex)_\n\n## Build Tips\nTeXstudio is a good editor with integrated PDF generation and preview.\n\nOtherwise, execute the following within the \\src directory.\n```\npdflatex Manual.tex\n```\n "
  },
  {
    "path": "jgnash-manual/src/Manual.tex",
    "content": "\\documentclass[letterpaper,12pt]{book}\n\\usepackage[titletoc]{appendix}\n\\usepackage[margin=.75in,heightrounded]{geometry}\n\\usepackage{fancyhdr}\n\n\\usepackage[T1]{fontenc}\n\\usepackage[utf8]{inputenc}\n%\\usepackage{lmodern}\t\t\t\t\t%font\n\\usepackage{fourier}                    %font\n\n\\usepackage{tabularx}\n\\usepackage{graphicx}\n\\usepackage{tikz}\n\\usepackage{awesomebox}\n\\usepackage[framemethod=TikZ]{mdframed}\n\\usepackage{menukeys}\n\\usepackage{float}\n\\usepackage{listings}\n\\usepackage{enumitem}\n\\usepackage{needspace}\n\\usepackage{xcolor}\n\\usepackage{textcomp}\n\\usepackage{tocloft}\n%\\usepackage{fontawesome}\n\\usepackage[singlelinecheck=false]{caption}\n\\usepackage[hidelinks, linktocpage=true]{hyperref}\n\n\\pagestyle{fancy}\n\n\\floatstyle{plaintop}\n\\restylefloat{table}                    % control table placement\n\n\\hyphenation{JGNASH}                    % prevent hyphenation\n\\hyphenation{jGnash}\n\n\\parindent0pt  \\parskip10pt            % make block paragraphs\n\n\\setlength\\headheight{16pt}\n\\setlist[description]{style=nextline}    % newline for descriptive lists\n\n\\setlength{\\aweboxrulewidth}{0.8pt}    % reduce width the awesome box vertical line\n\\setlength{\\aweboxcontentwidth}{1.0\\linewidth}\n\n\\renewcommand{\\arraystretch}{1.5}        % row height of tables\n\n\\renewmenumacro{\\directory}[>]{pathswithfolder}     % change icon\n\n\\renewcommand\\labelitemi{$\\bullet$}\n\\renewcommand\\labelitemii{$\\circ$}\n\\renewcommand\\labelitemiii{$\\bullet$}\n\n\\hypersetup{\ncolorlinks,\nlinkcolor={blue!80!black},\ncitecolor={blue!80!black},\nurlcolor={blue!80!black}\n}\n\n\\definecolor{light-gray}{gray}{0.98}    % define light gray backgrounds\n\n\\mdfdefinestyle{info}{                    % gray boxes\nlinecolor=gray,\nouterlinewidth=0.25pt,\nroundcorner=0pt,\ninnertopmargin=\\baselineskip,\ninnerbottommargin=\\baselineskip,\ninnerrightmargin=10pt,\ninnerleftmargin=10pt,\nbackgroundcolor=light-gray\n}\n\n\\lstset{%    \nbackgroundcolor=\\color{light-gray},\nframe=single,\nframesep=1em,\naboveskip=0pt,\nstringstyle=\\ttfamily,\nshowstringspaces = false,\nbasicstyle=\\scriptsize\\ttfamily,\ncommentstyle=\\color{gray},\nkeywordstyle=\\bfseries,\nndkeywordstyle=\\bfseries,\nidentifierstyle=\\ttfamily,\nnumbers=left,\nnumbersep=14pt,\nnumberstyle=\\tiny,\nnumberfirstline = false,\nbreaklines=true,\ncolumns=fixed,\nkeepspaces=true,\nframeround=ffff\n}\n\n\\lstdefinelanguage{JavaScript}{\nkeywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, const, let, async, await, if, in, while, do, else, case, break, from},\nndkeywords={class, export, boolean, throw, implements, import, this},\nsensitive=false,\ncomment=[l]{//},\nmorecomment=[s]{/*}{*/},\nmorestring=[b]',\nmorestring=[b]\"\n}\n\n\\pagenumbering{roman}\n\n\\title{\\textbf{jGnash 3.5.x Manual} }\n\\author{Craig Cavanaugh}\n\\date{\\today}\n\n\\begin{document}\n\n    \\begin{titlepage}\n\n        \\raggedleft % Right align the title page\n\n        {\\color{gray} \\rule{4pt}{\\textheight}} % Vertical line\n        \\hspace{0.08\\textwidth} % White space between the vertical line and title page text\n        \\parbox[b]{0.75\\textwidth}{ % Paragraph box for holding the title page text, adjust the width to move the title page left or right on the page\n\n        {\\Huge\\bfseries jGnash 3.5.x Manual}\\\\[2\\baselineskip] % Title\n        {\\Large\\textsc{craig cavanaugh}}\n\n        \\vspace{0.5\\textheight} % White space between the title block and the publisher\n\n        {\\noindent \\today}\\\\[\\baselineskip] % Publish date\n        }\n\n    \\end{titlepage}\n\n    \\tableofcontents\n    \n    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n    \\chapter{Legal}\\label{ch:legal}\n    {\\bfseries jGnash Manual}\n\n    Copyright~\\copyright~2001-2020 Craig Cavanaugh\n\n    jGnash comes with \\textbf{ABSOLUTELY NO WARRANTY}.\n\n    This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public\n    License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied\n    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along with this program.\n    If not, see \\href{http://www.gnu.org/licenses/}{http://www.gnu.org/licenses/}\n\n    Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation\n    License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections,\n    no Front-Cover Texts and no Back-Cover Texts.\n\n    A copy of the license is included in the section entitled ''GNU Free Documentation License''.\n\n    Linux\\textregistered~ is the registered trademark of Linus Torvalds in the U.S.\\ and other countries.\n    Windows is a registered trademark of Microsoft Corporation in the United States and other countries.\n    Java\\texttrademark~ and OpenJDK\\texttrademark~ are registered trademarks of Oracle and/or its affiliates.\n    \n    \n    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n    \\chapter{Conventions and Typographical Features}\\label{ch:conventions-and-typographical-features}\n    \n    Below are conventions used throughout this manual. With jGnash being a cross platform application, there\n    may be slight variations in the user interface behavior depending on your operating system. \n    \n    \\begin{tabularx}{\\linewidth}{|l|X|}\n        \\hline \n        \\textbf{Convention} & \\textbf{Usage} \\\\ \n        \\hline \n        \\hline \n        \\keys{CTRL + C} & User Interface buttons, Keyboard shortcuts and individual keys. The \\keys{CTRL} key is used generically for \\keys{CTRL} and \\keys{\\cmd} \n        depending on your operating system. \\\\ \n        \\hline \n        \\menu{File > Open} & Menu commands with the separator representing a level within the menu hierarchy.\\\\\n        \\hline \n        \\directory{directory} & Indicates a directory within your operating system. \\\\        \n        \\hline\n        \\hyperref[ch:conventions-and-typographical-features]{Link} & An internal or external hyperlink. \\\\\n        \\hline\n        \\texttt{Monospace} & Example text\t\\\\\n        \\hline               \n    \\end{tabularx}      \n    \n    \\mainmatter\n    \\pagenumbering{arabic}\n\n    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n    \\chapter{Introduction}\\label{ch:introduction}\n    \\includegraphics[scale=.6]{images/jgnash-logo-small}\n\n    jGnash is an open source application for personal finances.\n\n    jGnash enables you to record detailed account and transaction information using proven double entry accounting principles.\n\n    You do not have to be an accountant to understand or use jGnash, but jGnash provides options to make new and\n    experienced users feel comfortable using it.\n\n    jGnash's mission is personal fiance and it is not tailored for use as a business accounting application.\n    jGnash is being used by small businesses and clubs, but if you require business specific features, you may be\n    better served looking for a different solution.\n\n    \\section{Features}\\label{sec:features}\n    A brief list of features is below:\n\n    \\begin{itemize}\n        \\item Double Entry Accounting with reconciliation tools.\n        \\item Budgeting with multiple scenario options and export to spreadsheet capability.\n        \\item Investment Accounts and automatic import of Stocks, Bond, and Funds price history.\n        \\item Hierarchical accounts with automatic roll-up of totals and intelligent handling of mixed currencies.\n        \\item OFX, QFX, mt940, and QIF import with the ability to pre-process using JavaScript.\n        \\item Reminders and automatic transaction entry and notifications.\n        \\item Intelligent handling of multiple currencies and exchange rates with automatic online exchange rate updates.\n        \\item Printable reports with PDF and spreadsheet export capability.\n        \\item XML, Binary and SQL database file formats.\n        \\item Operates on most all modern PC operating system \\textit{(Java\\texttrademark~11 is required)}.\n        \\item jGnash will utilize multi-core processors.\n    \\end{itemize}\n    \n    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n    \\section{Installation}\\label{sec:installation}\n    jGnash is not currently distributed with an automatic installation tool.\n    You will be required to perform a couple of manual operations that are easily performed for those with a basic\n    understanding of how to use a zip file.\n    \n    Java\\texttrademark~11 or newer must be installed on your computer. jGnash has been tested with Java\\texttrademark~12\n    and Java\\texttrademark~13 if you have a need for a newer release.\n\n    \\textit{\\textbf{Use of an OpenJDK package is recommended over use of Oracle JDK due to licensing requirements}}\n\n    OpenJDK is a free and open-source implementation of the Java Platform that is downloadable from various sources.\n    Java installation is a simple matter of downloading the correct version for your operating system and using the\n    automated installer.\n\n    \\begin{description}\n        \\item[\\href{https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspot}{AdoptOpenJDK}]\n        Easy to install and recommended for Windows users. See Section~\\ref{sec:wininstall} for specifics. \\\\\n        \\texttt{[https://adoptopenjdk.net/index.html?variant=openjdk11\\&jvmVariant=hotspot]}\n        \\item[\\href{https://www.azul.com/downloads/zulu/}{Azul OpenJDK 11}]\n        A branded release that is easy to install for most users and is free to use. \\\\\n        \\texttt{[https://www.azul.com/downloads/zulu/]}        \n        \\item [\\href{https://jdk.java.net/11/}{OpenJDK}]\n        Will require manual installation and is free to use. \\\\\n        \\texttt{[https://jdk.java.net/11]}\n        \\item[\\href{https://www.oracle.com/technetwork/java/javase/downloads/index.html}{Oracle Java SE 11}]\n        Will require manual installation and licensing is required. \\\\\n        \\texttt{[https://www.oracle.com/technetwork/java/javase/downloads/index.html]}\n    \\end{description}\n\n    \\notebox{\n    If performing a manual installation of Java, The \\texttt{JAVA\\_HOME} Environment Variable must be set and the\n    Java \\directory{bin} directory must be in the execution path.\n    \\newpage\n    \\bigskip\n    If you have multiple versions of Java installed on your computer, The \\texttt{JAVA\\_HOME} Environment\n    Variable must point to Java 11 or newer and the related Java \\directory{bin} directory must be the only version\n    in the execution path.\n    Mixing JVM and JDK versions will confuse the boot loader.\n    }\n\n    After Java is installed, you are ready to install jGnash.\n    Simply open the zip file and extract the \\directory{jGnash} directory and it's complete contents to a directory of\n    your choice, and do not alter the files or locations.\n    I usually create a directory named \\directory{bin} in my home directory and keep\n    the \\directory{jGnash} directory in it to better organize my computer.\n    When upgrading between versions of jGnash, do not unzip to the same location and overwrite the existing files.\n    Use a new location or delete the existing files first.\n    \n    \\subsection{Windows Installation} \\label{sec:wininstall}\n    \n    Adopt OpenJDK works very well for jGnash primarily because it offers options to update the correct\n    registry keys so that jGnash.exe can find Java.  When installing, make sure the \\textbf{Set JAVA\\_HOME variable}\n    and the \\textbf{JavaSoft (Oracle) registry entries} options are selected for proper jGnash operation.\n        \n    \\begin{figure}[h]\n        \\caption{Adopt OpenJDK Install Options}\n        \\includegraphics[width=0.8\\linewidth]{images/adopt-open-jdk-install}\n    \\end{figure}\n   \n   \\notebox{\n   \tIf you are a Windows user with restricted write access to the file system, you may encounter a FileNotFound exception with an underlying \"Access is denied\" error.  \n   \tThis is caused by jGnash not being able to download and save the necessary JavaFX files for proper operation.\n   \t\\newpage\n   \t\\bigskip\n   \tThe solution is to place jGnash into your \\textbf{AppData} folder which should ensure write access.  The AppData folder is typically located within the \\textbf{C:\\textbackslash Users\\textbackslash user} folder which is hidden by default.  You can see the folder if you change the setting to show hidden files within Windows File Explorer.\n   }\n   \n   \n    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n    \\section{Starting jGnash}\\label{sec:starting-jgnash}\n\n    After the \\texttt{jGnash} directory has been extracted from the zip file, you should see several files in the directory.\n    Of interest at this point are the Bash script and \\texttt{exe} files.\n\n    If you are running on a Windows\\texttrademark~ based computer, you can simply double click on the \\texttt{jGnash.exe} file to\n    start jGnash.\n\n    If you are running on a Un*x or BSD (macOS) based system you can start jGnash from a terminal as shown below using\n    the included Bash script.\n    You can also create your own application launcher using the Bash script in your desktop environment of choice.\n\n    \\begin{mdframed}[style=info]\n        \\texttt{./jGnash}\n    \\end{mdframed}\n\n    jGnash has several advanced features such as running as a portable application or using jGnash as a multi-user home\n    networked application. These advanced features are accessible via the command line.\n    Please see \\hyperref[ch:cmdOptions]{Command Line Options} for more details.\n\n    \\section{Running for the First Time}\\label{sec:running-for-the-first-time}\n    A license acceptance screen will be displayed the first time you start jGnash.\n    jGnash will not run unless the license is accepted.\n    The short of the license agreement is jGnash is a freely available program comprised of other freely available software,\n    and should anything bad happen during use, the authors are not libel for any damages.\n    The license also details how jGnash may be distributed and used.\n\n    \\notebox{\n    If the license agreement sounds daunting, take a look at the license agreements of commercially available personal\n    finance applications and you will see similar agreements.\n    Myself and just about every other person making software available for free or purchase tries their best to ensure the\n    software they create works well and as intended.\n    Sometimes bugs do creep in and it does not work quite as planned.\n    The advantage of free software is you generally have direct access to the authors, and you have a much larger voice in\n    helping the application grow and evolve over time.\n    }\n\n    \\section{Getting Help and Giving Back}\\label{sec:getting-help-and-giving-back}\n    The intent of this user guide is to get you off to a good start using jGnash.\n    Despite my best attempts, there are those who need a little bit of extra help or have a special need or\n    circumstance and require the help of others that have already been around the block a few times.\n\n    The best place to start is the jGnash user group hosted at \\\\\n    \\href{https://groups.google.com/forum/#!forum/jgnash-user}{jGnash-User} \\texttt{[https://groups.google.com/forum/\\#!forum/jgnash-user]}.\n\n    The user group contains a well rounded group of individuals who can help answer just about any question.\n    As a courtesy to others, I encourage you to search the group prior to asking a question to see if it's already\n    been answered.\n\n    If you have found a bug, or have suggestions for improvement, the group page has links to a bug and feature request\n    tracker that can be used to log and track your requests.\n    The group forum can be used to post a bug or request, but use of the tracker ensures the request is not lost in\n    the mix of discussions.\n\n    If you are well versed in use of jGnash and other personal finance applications, you are encouraged to give back a\n    little time and contribute your experience to the group and help others.\n\n    The~\\nameref{ch:frequently-asked-questions} chapter will help answer a few common questions.\n      \n    % ======================\n    % Getting Started\n    % ======================\n    \\chapter{Getting Started}\\label{ch:getting-started}\n    The basic elements of jGnash are Accounts, Currencies, Securities, and Transactions.\n    Every account is assigned one currency and every transaction is associated with at least one account.\n    Investment Accounts and Investment Transactions will be associated with a Security.\n\n    jGnash supports various Account types that can be arranged into a flexible hierarchical structure.\n    The Accounts may be arranged by financial institution, by type, or some other structure.\n    It's good practice to organize your Income and Expense Accounts into a logic arrangement that allows you to drill\n    down into a layer of more detail.\n\n    The balance of an Account will roll up into it's parent Account within the Accounts view as well as in some reports.\n\n    Below is a typical Expense Account arrangement that allows you to differentiate between different type of automotive\n    and food related expenses, but roll them up into more macro level expenses.\n\n    \\begin{mdframed}[style=info]\n        \\begin{itemize}\n            \\item Expense Accounts\n            \\begin{itemize}\n                \\item Automobile\n                \\begin{itemize}\n                    \\item Fuel\n                    \\item Insurance\n                    \\item Service\n                \\end{itemize}\n                \\item Food\n                \\begin{itemize}\n                    \\item Dining Out\n                    \\item Groceries\n                \\end{itemize}\n            \\end{itemize}\n        \\end{itemize}\n    \\end{mdframed}\n\n    The Account structure may be changed and reordered later, but Accounts may not be deleted if they contain transactions.\n\n    Transactions are used to record daily expenditures as well as income from the sale of personal items, investments, or\n    paychecks.\n\n    If you are familiar with other personal finance applications, you may notice that jGnash uses income and expense\n    \\textit{accounts} instead of income and expense \\textit{categories}.\n    Functionally, there is no difference, other than jGnash allows you see a detailed transaction register of the\n    income and expense accounts as easily as you would look at your bank accounts.\n\n    \\section{Editing Environment}\\label{sec:editing-environment}\n    The jGnash editing environment is not much different then in other application with the exception that it provides a\n    few shortcuts to speed up entry of transactions.\n\n    jGnash knows and understands just about any known locale and country setting.\n    Depending on your settings, the decimal symbol will change accordingly as well as the displayed format of dates.\n\n    Error checking of required form fields is performed real-time.\n    As information is entered you will notice buttons such as \\keys{Enter} are enabled and disabled based on validity\n    of what has been entered.\n\n    \\subsection{Date Fields}\\label{subsec:dateFields}\n    The date field is freely editable and jGnash will make the best attempt at interpreting an invalid entry, but the results\n    are indeterminate.\n    Clicking on the button to the right of the field will display a calendar dialog where you can select a date as well.\n\n    The field is flexible enough to allow use of multiple keys as the field separators regardless of the current locale.\n    This is done to make entry easier on compact keyboards.\n    The usable field separator characters are defined in the table below.\n\n    \\includegraphics[scale=.8]{images/date-entry}\n\n    Also, dates can be modified using the keyboard shortcuts defined below.\n\n    \\begin{table}[H]\n        \\begin{tabular}{ll}\n            \\hline\n            \\multicolumn{1}{|l|}{\\textbf{Keys / Mouse}}                            \t& \\multicolumn{1}{l|}{\\textbf{Function}}            \\\\ \\hline \\hline\n            \\multicolumn{1}{|l|}{\\keys{{+}} \\keys{\\arrowkeyup}, Mouse Scroll Up}   \t& \\multicolumn{1}{l|}{Increase the date by one day} \\\\ \\hline\n            \\multicolumn{1}{|l|}{\\keys{-} \\keys{\\arrowkeydown}, Mouse Scroll Down} \t& \\multicolumn{1}{l|}{Decrease the date by one day} \\\\ \\hline\n            \\multicolumn{1}{|l|}{\\keys{PgUp}, Mouse Thumb Wheel Up}                \t& \\multicolumn{1}{l|}{Increase the date by one month} \\\\ \\hline\n            \\multicolumn{1}{|l|}{\\keys{PgDn}, Mouse Thumb Wheel Down}              \t& \\multicolumn{1}{l|}{Decrease the date by one month} \\\\ \\hline\n            \\multicolumn{1}{|l|}{\\keys{t} \\keys{T}}                                \t& \\multicolumn{1}{l|}{Change to today's date} \\\\ \\hline\n            \\multicolumn{1}{|l|}{\\keys{,} \\keys{.} \\keys{/} \\keys{\\textbackslash}} \t& \\multicolumn{1}{l|}{Valid field separator for all locales} \\\\ \\hline\n        \\end{tabular}\n        \\caption{Date Entry Shortcut Keys}\n    \\end{table}\n\n    \\subsection{Number Fields}\\label{subsec:numberFields}\n\n    Numerical entry in jGnash is as easy as typing the desired value into the field.\n    Decimal separators are handled according to the configured locale.\n\n    \\includegraphics[scale=0.8]{images/basic-decimal-entry} \\hspace{10pt} \\includegraphics[scale=0.8]{images/advanced-decimal-entry}\n\n    You may also enter arithmetic operators and calculate values within the entry field.\n\n    The arithmetic operators that may be used are \\keys{(} \\keys{)} \\keys{{+}} \\keys{{-}} \\keys{$\\star$} \\keys{/} \\keys{.} \\keys{,}~.\n\n    Traditional arithmetic operator precedence is followed for all calculations.\n\n    \\section{Creating a New File}\\label{sec:creating-a-new-file}\n\n    When you start jGnash for the first time, you will be presented with a very simple screen.\n    At the bottom of the screen application messages and background process indicators are shown.\n\n    \\subsubsection*{Creating a new file is done in 5 steps using the wizard}\n    \\begin{enumerate}\n        \\item Create a new file using the \\menu{File > New} command and follow the prompts in the new file wizard.\n        When given the choice to select the \\hyperref[subsec:fileTypes]{storage type}, leave it as the default value for now.\n        A default file name and location will be provided that you can change if desired.\n        If the file already exists, you will be warned you are about to overwrite it.\n        \\item After selecting the \\hyperref[subsec:fileTypes]{storage type} and file name, you will be asked to choose\n        the default currency.\n        The default currency can be changed at a latter time, and if for some reason your currency of choice is not\n        available, you can create a custom currency and set it as the default after the file is created.\n        \\item Next you can choose the currencies that are available for use.\n        Currencies may be added and removed as needed at a later time if needed.\n\n        If needed, custom currencies may be added using the \\menu{Currencies > Add/Remove} command.\n        As locales change, default currency availability will change as Java is updated.\n        The typical need for a custom currency is to support legacy accounting information as countries standardize on the Euro.\n        \\item After choosing the available currencies, default accounts can be selected if desired.\n        If you are new to personal finance software, the defaults will be a good starting point.\n        The accounts structure can be easily changed after creating the file to accommodate your own personal needs.\n        \\item The last step is the Summary page of the wizard.\n        Verify everything is to your liking and click on the \\keys{Finish} button to create your new file.\n    \\end{enumerate}\n\n    After the file is created, you are now ready to change, add, or remove accounts as need and begin entering transactions.\n\n    \\begin{mdframed}[style=info]\n        Encryption and password protection options do not exist in jGnash with the exception of a clear text password\n        for client/server operation.\n\n        Encryption is difficult when it involves exporting and distributing software throughout the world.\n        jGnash is designed to support many nationalities, so control of distribution would be become very difficult if\n        encryption was integrated.\n\n        If you do have the desire to encrypt your jGnash data, the best choice is to use the encryption capabilities\n        of your operating system or\n        install a freely available third party encryption tool.\n    \\end{mdframed}\n\n    \\subsection{File Types}\n    \\label{subsec:fileTypes}\n    jGnash supports different file types for storing data.\n    File types can be easily changed by using the \\menu{File > Save As} command and naming the new file with the\n    appropriate file extension.\n\n    The current file will be saved in the new format and automatically opened.\n\n    Regardless of file type used, jGnash automatically saves the data if changed every 30 seconds to minimize the\n    chance of accidental data loss.\n\n    \\notebox{\n    If you are using jGnash in the Client/Server mode, all changes are committed immediately.\n    }\n\n    \\subsection{H2 and HyperSql SQL Databases}\\label{subsec:h2-and-hypersql-sql-databases}\n    An H2 or HyperSql SQL database is required when using the client / server functionality of jGnash.\n    jGnash embeds the database server so that no additional configuration or installation of software is required\n    to use a relational database.\n\n    The relational database may be used for a single user.\n    If startup and shutdown performance is important to you, then the binary file format described below is a better choice.\n\n    The advantage of the relational database outside the requirement for client / server capability is the ability\n    to use several available tools to browse\n    and query your jGnash data.\n    Also, a relational database is more tolerant of system crashes or power outages vs.\\ use of an XML or Binary file.\n\n    The disadvantages of the relational database is a bit slower operation, larger file sizes, and increased memory consumption.\n\n    If using the H2 Database and operating over a network using Client/Server mode, you have the option of enabling\n    encryption for network communication.\n    This will not encrypt your database file.\n    See the Command line options for specifics.\n\n    \\notebox{\n    The default administrator for a jGnash relational databases is JGNASH and is not configurable at this time.\n    }\n\n    \\subsection{XML File}\\label{subsec:xml-file}\n    XML file format is human readable and easily read by other applications at the expense of a considerably larger file size.\n    Memory usage is less when using the XML file format, but certain operations may take longer.\n    The advantage of the XML file is easier parsing and manipulation of the file using another program external to jGnash.\n    If you have a large amount of data, jGnash will use less system memory when using the XML file format.\n\n\n    \\notebox{\n    The XML format is also used for creating automatic backups of jGnash files if enabled.\n    }\n\n    \\subsection{Binary File}\\label{subsec:binary-file}\n    The binary file format is the most compact file format and will open and close the quickest.\n    This is the recommended file format if you do not need client / server functionality and you are using a laptop\n    or a workstation with a UPS\\@.\n\n    \\newpage\n    \\section{Accounts}\\label{sec:accounts}\n    Accounts are what you use to organize how you save and spend your money, and where it comes from.\n    Account structures can be changed to organize the display of information to suit your specific needs.\n\n    Typically, you will have a separate jGnash account for each savings, checking, investment account, etc.\\\n    that you have at a financial institution.\n    Accounts can be organized under \"placeholder\" accounts to add different levels of organization.\n\n    For example, if you would like to see a summary of your accounts by financial institution, you can create a placeholder\n    account that represents a single financial institution and group your savings and checking accounts from that particular\n    institution.\n\n    Maybe you want to see all savings accounts grouped together and checking accounts grouped together.\n    It's just a mater of creating placeholder accounts for checking and savings and placing the respective accounts under them.\n\n    The account structure can be easily changed at any time with the exception of removing accounts after they have\n    transaction in them.\n    Transactions must be manually deleted from an account before you can remove it.\n\n    %\\newpage\n    \\subsection{Creating Accounts}\n    \\label{subsec:creatingAccounts}\n    A new account is created by clicking on the \\keys{New} button in the toolbar of the account list view shown below.\n\n    \\begin{figure}[h]\n        \\caption{New Account Button}\n        \\includegraphics[width=0.4\\linewidth]{images/new-account}\n    \\end{figure}\n\n    A new dialog box will be displayed that allows you to create a new account to suit your particular needs.\n\n    \\begin{figure}[H]\n        \\caption{New Account Dialog}\n        \\includegraphics[width=0.55\\linewidth]{images/account-dialog}\n    \\end{figure}\n\n    \\subsubsection*{Account Information}\n    \\begin{description}[style=nextline]\n        \\item[Name]\n        The name for the account.\n        The name will be used to identify the account when creating transactions.\n        Account names do not have to be unique, but may not be left blank.\n        \\item[Description]\n        A description for the account.\n        This field may be left blank if desired.\n        \\item[Account Number]\n        The Account Number field is generally used for storing the account number provided by your financial institution.\n        This field may be left blank if desired.\n        \\item[Account Code (General Ledger Code)]\n        The Account Code field will default to a value of 0 and must be a numerical value.\n        This value will also control the displayed order of accounts within the same branch.\n        If the account code is left as a value of zero, sort order will be deferred to an alphanumeric sort against the account names.\n        Best practice is to either use an assigned Account Code for all accounts within the same branch or leave the\n        code as a value of 0 for all accounts.\n        Otherwise, sort order may appear to be random.\n        \\item[Bank ID]\n        This is the identification number of the financial institution and is generally used as an identifier when importing OFX files.\n        This field may be left blank if desired.\n        \\item[Currency]\n        The currency for the account.\n        Account currencies cannot be changed if the account contains transactions.\n        \\item[Securities]\n        This button will display a dialog that allows you to add and remove the available Securities for the account.\n        This applies only to Investment accounts.\n        \\item[Account Type]\n        Account Type determines what the account can be used for and the type of transactions that can be created.\n        An Account's type may be changed after it is created to another type only if it is similar in function.\n        For example, an Investment Account can be change to a Mutual Fund account and vice-versa, buy they may not be\n        changed into Bank or Credit accounts.\n        \\item[Account Options:Locked]\n        If selected, the account will be locked and further changes will be prevented.\n        This is useful to lock accounts that have been closed while retaining your historical data.\n        \\item[Account Options:Placeholder]\n        If selected, child accounts may be placed under this account and the account will not accept transactions.\n        This is useful for organizing accounts into a hierarchical structure.\n        \\item[Account Options:Hide Account]\n        If selected, the account is hidden from view if the hide account filter is enabled.\n        \\item[Account Options:Exclude From Budgets]\n        If selected, the account is hidden from view of the budgeting tool.\n        \\item[Parent Account]\n        Clicking on this button will display a dialog that lets you select the account this account resides under.\n        If you want the account to be placed at the top most level, then choose the Root account as the parent.\n        Parent accounts may be changed as needed to suit your needs.\n        \\item[Notes]\n        Room for extra information about the account if desired.\n    \\end{description}\n\n    At any later time, the \\keys{Modify} button may be used to change an existing account.\n    If the account contains transactions, certain options may not be changed.\n\n    \\notebox{\n    jGnash does not have a place to specify an opening balance in keeping with correct practices for double entry accounting.\n    \\newpage\n    \\bigskip\n    \\textbf{However, it is still possible to set and opening balance:}\n    \\newpage\n    \\bigskip\n    To add an opening balance, create a transaction with an appropriate deposit or withdrawal against an\n    Equity Account of your choice.\n    Most people will choose or create an \"Opening Balance\" account.\n    \\newpage\n    \\bigskip\n    This follows well known accounting practices.\n    \\newpage\n    \\bigskip\n    \\textit{If you are not concerned about correct accounting practices, you can create an Adjustment Transaction instead.}\n    }\n\n\n    \\section{Account Types}\n    jGnash allows use of several account types to make organization easier.\n    The account type chosen can have a significant impact on how reports are generated and displayed, and the types\n    of transactions you can create.\n\n    \\subsection{Asset}\n    Asset accounts are intended to be used to track the value of durable items such as houses, cars, boats, collections, etc.\\\n    Value of items can be adjusted over time against Income accounts to show gain or loss of value.\n    If you were to sell an item and convert it cash, the sale of the item can be tracked against the Asset accounts containing the item.\n\n    \\subsection{Bank}\n    Bank accounts are used for the savings accounts you would have at a bank.\n\n    \\subsection{Cash}\n    Cash accounts represent the cash you carry with you.\n    Cash accounts are also good for representing deposits and withdrawals from Flexible Spending Accounts.\n\n    \\subsection{Checking}\n    Checking accounts are used for the checking account you would have at a bank.\n\n    \\subsection{Credit}\n    Credit accounts are used to record purchases and payments made to a credit card account.\n    Credit accounts are primary used for short-term liabilities and great for representing overdraft and line of\n    credit accounts at banks.\n\n    \\subsection{Equity}\n    Equity accounts are used to record opening account balances against.\n    Typically, you will have only one Equity account.\n    Equity accounts are representative of another accounts net worth at the time you begin tracking it's value.\n\n    \\subsection{Expense}\n    Expense accounts are used to record expenses such as food, utilities, taxes, investment expenses, etc.\n\n    \\subsection{Income}\n    Income accounts are used to record income such as salary, dividends, investment income, etc.\n\n    \\subsection{Investment}\n    \\label{sub:investaccount}\n    Investment accounts are used to buy and sell Securities.\n    Investment accounts can be used to track 401k, IRA's, etc.\n    Please see \\hyperref[ch:securities]{Securities} for details specific to setting up Securities.       \n\n    \\begin{itemize}\n        \\item Investment Accounts have a cash balance if you buy or sell transactions against the account.\n        \\item Investment purchases and sales fees can be made against the cash balance of the investment account or\n        other specified accounts.\n        \\item Multiple investment fee entries per transaction may be entered.\n        \\item Multiple gains/loss entries per transaction may be entered.\n        \\item Investment Accounts support multiple securities.\n        \\item Investment Accounts can be used to model an on-line brokerage account.\n    \\end{itemize}\n\n    \\subsection{Liability}\n    Liability accounts are used to track long term loans or liabilities.\n    Liability accounts have the added feature of allowing you to set-up a loan payment that takes some of the\n    effort out of entering periodic loan payment transactions.\n\n    \\subsection{Money Market}\n    Money Market accounts are typically a high interest yield account with withdrawal rules or limitations.\n    They are generally used for long term savings accounts with the intent of keeping your cash readily accessible.\n    Please see \\hyperref[ch:securities]{Securities} for details specific to setting up Securities.\n\n    \\subsection{Mutual Fund}\n    Mutual fund accounts are a specialized version of an Investment account and generally used to track mutual fund type\n    investments.\n    Please see \\hyperref[ch:securities]{Securities} for details specific to setting up Securities.\n\n    \\subsection{Simple Investment}\n    Simple investment accounts are for investments where you do not actively manage or are able to track purchases and\n    sales of securities.\n    The typical scenario would be a company pension plan that only provides cash balance information.\n    Sometimes, these types of investment accounts are called Annuities or Guaranteed Retirement Accounts\n\n    \\subsection{Root}\n    The root account is the top level account that holds all other accounts.\n    You cannot remove or modify the root account.\n    Normally, it is not visible unless you are changing the account structure.\n\n    \\section{Entering Transactions}\n    jGnash follows well know double entry accounting practices while reducing the complexity of transaction entry.\n\n    If you are familiar with other personal finance applications, you may notice that jGnash uses income\n    and expense accounts instead of income and expense categories. Functionally, there is no difference,\n    other than jGnash allows you see a detailed transaction register of the income and expense accounts as\n    easily as you would look at your bank accounts.\n\n    When creating your file, if you selected the available default accounts, you will have income and expense\n    accounts to work with.\n    These accounts can be changed to suit your needs at any time.\n\n    The benefit of double entry accounting in jGnash is the ability to use reports and charts to see where your\n    money is going and coming from.\n    Single entry transactions hide those details and make tracking difficult and tedious.\n\n    \\begin{mdframed}[style=info]\n        For simplicity, the terms used here to discuss changes in account balances will be \\textbf{Increase} and \\textbf{Decrease}.\n\n        Business accounting practices use the Credit and Debit terms which can appear to be backwards depending on account type\n        and cause confusion for those who are not familiar.\n    \\end{mdframed}\n\n    \\subsection{Common Transaction Properties}\n    The properties below are common to all basic transaction types.  \\textit{Investment transactions will\n    require additional information not covered in this section.}\n\n    \\subsubsection*{Payee}\n    The Payee is almost always the name of a person or business. The Payee field may be empty, but\n    it is good practice to always use a payee for a transaction entry. jGnash will automatically learn\n    and auto complete a transaction using the Payee field. This helps aid speed of transaction entry and\n    helps to establish consistency making searching and filter much easier.\n\n    \\subsubsection*{Number}\n    A transaction may be assigned a number or abbreviated description.\n    If working with a Checking account, the number is almost always the same as the check number you wrote.\n    Use of a check number also makes reconciliation against a bank statement easier.\n\n    \\subsubsection*{Date}\n    Every transaction has a date that is usually the date the transaction occurred.\n    Some people will choose to edit the transaction to match the posting date.\n\n    Please review the \\hyperref[subsec:dateFields]{Date Fields} shortcuts that may be used to save a significant\n    amount of time when selecting and editing dates.\n\n    \\notebox{\n    jGnash also maintains an internal timestamp for the actual date and time the transaction was created or last modified\n    for auditing and tracking purposes.\n    }\n\n    \\subsubsection*{Memo}\n    The Memo is a brief description of the transaction so you can remember what it was.\n    jGnash will automatically learn the Memos you commonly use.\n    This helps aid speed of transaction entry and helps to establish consistency making searching and filter much easier.\n\n    Split transactions have an option to Concatenate or automatically merge the Memos of the split entries.\n\n    \\subsubsection*{Amount}\n    This is the transaction amount. If entering a multi-currency transaction, this field will be expanded to handle\n    the correct exchange of currencies.\n\n    The \\hyperref[subsec:numberFields]{Number Fields} section details how to enter amounts using mathematical operations\n    in the same manner as a calculator.\n\n    \\subsubsection*{Reconciliation State}\n    The Cleared CheckBox at the bottom of the register form is a tri-state box that allows you to toggle through\n    all three of the reconciliation states described below.\n\n    \\paragraph*{A jGnash transaction has three reconciliation states}\n    \\begin{description}[style=nextline]\n        \\item[Not Reconciled]\n        The transaction has not been reconciled against a bank or online statement\n        \\item[Cleared]\n        The intended use is for manual reconciliation of a specific transaction prior to a full reconciliation for the period.\n        \\item[Reconciled]\n        The transaction has been reconciled manually or through use of the Reconciliation tool\n    \\end{description}\n\n    \\subsubsection*{Transaction Attachments}\n    jGnash allows the attachment of an image file to the transaction using the chain link button at the bottom of the\n    transaction form. The attachment will be moved to a managed directory call \\directory{attachments} located in the\n    same directory as your jGnash file. The eye button allows you to view the attachment and the broken chain loop\n    button allows for removal of the attachment.\n\n    \\subsection{Double Entry Transactions}\n    A double entry transaction follows standard accounting practices. A double entry transaction will always\n    increase the balance of one account and decrease the balance of another account. The amount of the change will\n    always equal and opposite in value.\n\n    \\needspace{5\\baselineskip}\n    \\begin{mdframed}[style=info]\n        Double entry transactions using accounts with different currencies will not be equal and opposite numerically due\n        to exchange rates between currencies. jGnash automatically handles the difficult part of the currency exchange\n        within the transaction.\n    \\end{mdframed}\n\n    A double entry transaction normally begins within the register of some type of Cash or Bank account.\n    In the example below, a Withdrawal is being made from a Cash account. The balance of the account will be decreased\n    by \\texttt{25.67} and the balance of the \\texttt{Expense Accounts:Automobile:Fuel} will be increased by \\texttt{25.67}.\n\n    The account to withdraw funds from (Decrease) or deposit to (Increase) is selected in the Account ComboBox.\n\n    \\begin{figure}[h]\n        \\caption{Double Entry Transaction}\n        \\includegraphics[width=0.9\\linewidth]{images/basicDoubleEntry}\n    \\end{figure}\n\n    \\subsection{Split Entry Transaction}\n    A Split Entry Transaction is a Double Entry Transaction, but the amount is split across multiple accounts.\n\n    A Split Entry Transaction is started by clicking the \\menu{Splits} Button. A dialog will be shown that allows an entry\n    for multiple accounts to be made.\n\n    \\begin{figure}[h]\n        \\caption{Split Transaction Entry}\n        \\includegraphics[width=1.0\\linewidth]{images/splitTransactionEntry}\n    \\end{figure}\n\n    Notice the \\menu{Concatenate Memos} Checkbox. If the Checkbox is selected, the Transaction memo will be a generated list\n    of the unique memos of the split entries. This saves entry time for the Transaction and reduces the file size.\n\n    Editing of the Transaction amount will be disabled as it is the sum of the Entries. The Memo field will be disabled\n    as well if the \\menu{Concatenate Memos} in the Split Transaction dialog was selected.\n\n    If a memo is not entered for a Split Transaction and the \\menu{Concatenate Memos} button is not selected, the\n    Transactions register will display the memo of the first Split Entry.\n\n    A Split Transaction may consist of multiple Deposits and Withdrawals as shown in Figure~\\ref{fig:mixed-split-trans}. \n    This is a typical example of a paycheck where insurance and taxes are deducted from the gross salary.\n\n    \\tipbox{\n        Options specfic to reconsiliation of Split transactions will be covered in a later Chapter.\n        \\newpage\n        \\medskip\n        You have the option of manual reconsiliation, but there are other ways to speed up the process.\n    }\n\n    \\begin{figure}[h]        \n        \\caption{Mixed Split Transaction Entry} \\label{fig:mixed-split-trans}\n        \\includegraphics[width=1.0\\linewidth]{images/mixedSplitTransaction}               \n    \\end{figure}\n\n    \\subsection{Single Entry / Adjustment Transaction}\\label{subsec:single-entry-/-adjustment-transaction}\n    A Single Entry Transaction is primarily used to adjust \\textit{(fudge)} an Account balance when you simply do not\n    have the information to correctly balance the account.\n\n    In Figure~\\ref{fig:adjustment-trans}, you can see a negative amount of \\texttt{-1.23} was used to decrease the balance of the account.\n\n    \\begin{figure}[H]\n        \\caption{Adjustment Transaction Entry} \\label{fig:adjustment-trans}\n        \\includegraphics[width=1.0\\linewidth]{images/adjustmentTransaction}\n    \\end{figure}\n\n\n    The half black and half white circular button at the bottom of the form is a utility to help convert the\n    Single Entry transaction into a Double Entry Transaction after you find the needed information.\n\n    \\subsection{Transfer Transaction}\n    The Transfer tab provides a slightly faster way to move money between accounts without requiring as much information.\n    If go back to edit the Transaction, the edits will be performed within the Deposit or Withdrawal forms.\n    \n    \\section{Investment Accounts}\n    Investment Accounts need to be populated correctly if the Portfolio report is to work correctly.\n\n    \\begin{itemize}\n        \\item Accounts with prior history should purchase securities from the \"Opening Balance\" Equity account. Ensure the quantity of\n        securities is correct and use the current price.\n        \\item Cash balances should be transfers from the \"Opening Balance\" Equity account.\n        \\item Dividends, sells, etc.\\ should occur after opening securities and cash balances are established.\n    \\end{itemize}\n\n    Please see the \\hyperref[ch:securities]{Securities} chapter for details specific to setting up Securities.\n\n    \\chapter{Reminders}\n    If you find yourself recreating the same transaction on a regular basis, Reminders can be created to automate the process.\n\n    \\includegraphics[width=0.8\\linewidth]{images/reminders}\n\n    The table shown above displays all the Reminders you have created with some basic information.\n\n    \\begin{itemize}\n        \\item The \\textbf{Last Posted} column shows the last date the Reminder was processed. It will be empty for a new Reminder.\n        \\item The \\textbf{Due} column shows the next due date if a configured end date has not been reached.\n        \\item The \\menu{Modify} button is for editing the selected Reminder.\n        \\item The \\menu{Execute Now} button will process the selected Reminder if it is due.\n        \\item The \\menu{Check Reminders} button looks for any pending Reminders and displays them. If there are not\n        any pending Reminders, the dialog will not be displayed.\n    \\end{itemize}\n\n    \\section{Notification of Reminders}\n    The pending Reminders dialog will be shown shortly after jGnash has been started or when manually checked\n    as described earlier.\n\n    \\includegraphics[width=0.8\\linewidth]{images/remindersPopupDialog}\n\n    \\begin{itemize}\n        \\item The \\menu{Remind Me Later} button lets you to snooze the dialog for a selectable period of time if you do not want to\n        process the transactions at that time.\n        \\item The \\menu{Acknowledge Selected} button processes \\textit{only} the transactions marked in the \\textbf{Approve} column.\n    \\end{itemize}\n\n    Below the \\textbf{Approve} column are buttons to make it easier to mark a large number of transactions for approval.\n\n    \\section{Creating New Reminders}\n\n    Creating new Reminders is a process of filling in some basic information, creating your transaction, and configuring the\n    frequency of the Reminder.\n\n    An Account must be selected for the Reminder and fields exist to provide a description and additional notes.\n    A transaction is optional\n    A Reminder without a transaction will simply be a Reminder to do something.\n\n    The typical approach for assigning the Account is to use a bank or cash account of some sort. When you create the\n    transaction, it will be against an expense or income account unless it is a periodic transfer between accounts.\n\n    However, if you prefer to organize Reminders by expense type or account, you can pick an expense or income\n    account and create the transaction against a bank account.\n    Regardless of approach, be consistent to avoid confusion later.\n\n    \\includegraphics[width=0.8\\linewidth]{images/remindersNewDialog}\n\n    Configuring the Frequency of a Reminder is easy despite there being a lot of options. The \\textbf{Date of first payment}\n    field establishes the first date the Reminder process starts as well as the day of the month it occurs on.\n\n    The \\textbf{Enabled} check box will be selected by default. Unselecting it will pause the Reminder should you want\n    to disable it for a period of time instead of deleting it.\n\n    The \\textbf{Month} frequency tab provides an option of \\textbf{Month By Day} or \\textbf{Month by Date}.\n    As an example, selecting \\textbf{Month By Day} indicates you may want a Reminder to occur every 2nd Tuesday of the month.\n    Selecting \\textbf{Month by Date} would indicate you want a Reminder to occur of the 15th of every month.\n\n    Creating a transaction for a Reminder is identical to creating a transaction in the register. The Date field will be\n    ignored and replaced by the recurrence date of the transaction defined in the Frequency section.\n\n    \\includegraphics[width=0.85\\linewidth]{images/remindersNewTransaction}\n\n    The \\textbf{Entry} section of the New Reminder form gives you option to automatically enter a new transaction a defined number\n    of days prior to the date defined by the Frequency section. When enabled, you will not see the Notification dialog.\n    Typical use is for automatic withdrawals or deposits into an account.\n\n    \\section{Tips}\n\n    \\subsection{Quick Generation of new Reminders}\n    New reminders may be created from an existing transaction.\n\n    Within the Register, right click on a transaction to display the context menu.\n    From the context menu, the \\menu{Create new reminder} command will generate a new reminder using the existing\n    transaction as a template and display the New Reminder Dialog.\n    You will need to ensure the Frequency parameters are correct and simply click the\\menu{OK} to save it.\n\n    \\includegraphics[width=0.8\\linewidth]{images/remindersQuickTransaction}\n\n    \\subsection{Special Dates}\n    Be careful when creating Reminders near the end of the month with a Month frequency.\n\n    \\begin{itemize}\n        \\item What happens if you select February 29th (Leap Day)?\n        \\item Do you receive a paycheck every two weeks (26 pay periods) or twice a month (24 pay periods)?\n    \\end{itemize}\n\n    jGnash makes the best attempt to ensure correctness of dates, but minor date shifts can occur should you select leap days,\n    the 1st day of a 5 week month, etc.\n\n    \\chapter{Budgets}\n\n    jGnash has a budgeting feature that makes it easy for you to define spending and income goals by account and bump those goals up against your actual transactions.\n    A compact graphical overview of each budgeting period is provided to highlight how well you are following your budget based on selectable periods.\n\n    Tracking how well you follow your budget can be an eye opening experience and can lead to better financial health.\n\n    \\notebox{\n    jGnash budget periods follow rules established by ISO 8601. Weeks begin on Monday and the first week of the year may begin\n    with the last few days of the prior year.\n    }\n\n    \\section*{Budget Features}\n\n    \\begin{itemize}\n        \\item Multiple budgets are supported and may be copied making it easy to try out different scenarios and create year specific budgets if desired.\n        \\item Allowed accounts for budget are limited to Income and Expense accounts.\n        \\item Accounts may be excluded from budgets by setting the exclude flag in the account properties dialog. Sub-accounts will not be displayed if the parent account is excluded.\n        \\item The reporting period for budgets may be daily, weekly, bi-weekly, monthly, or quarterly and can be changed as needed.\n        \\item The per account budget goals may also be entered in daily, weekly, bi-weekly, monthly, or quarterly periods and are independent of the budget reporting period.\n        \\item The budget may be exported to a spreadsheet.\n    \\end{itemize}\n\n    \\tipbox{\n    The reported period of the budget is independent of the per account budget goal period.\n    \\newpage\n    \\medskip\n    Example: Your salary is paid in bi-weekly intervals, but you want to see your budget reported by month.\n    You can change the period for the income account to weekly or daily and enter your salary.\n    \\newpage\n    \\medskip\n    When reported by month, jGnash automatically handles the difference in the periods and distributes your bi-weekly\n    salary income across monthly boundaries.\n    }\n\n    \\section{Graphical Overview}\n\n    The main budget panel is shown below.\n\n    \\includegraphics[width=1.0\\linewidth]{images/budget-overview}\n\n    The width of the account column is adjustable by placing the cursor between the Account header and the period header\n    columns and then clicking and dragging the mouse cursor right or left.\n\n    At the top of the panel, a toolbar exists that allows you to change how much information is displayed, modify the\n    budgets, and export the active budget to a spreadsheet.\n\n    The budget drop down list lets you quickly select between different budgets you have created.\n    The \\menu{Budget Manager} button displays a dialog that let you create, duplicate, and delete budgets.\n\n    The year spinner allows you to bump the selected budget up against the selected year's transaction data.\n    The selected year also effects the calendar periods when editing period amounts.\n\n    \\subsection{Properties}\\label{subsec:properties}\n    The \\menu{Properties} button will display the dialog shown below with various options for the active budget.\n\n    The period used for the budget display can be changed in this dialog as well as the budget description.\n    You may also select the account groups that are visible for the selected budget.\n\n    The \\textbf{Start Month} property sets the month you begin a new budget. For most, it's the beginning of the year, but\n    others may prefer a month such as April which is tax season for some. If you are new to jGnash and you've started\n    mid-year, then the month you being is a good start. The \\textbf{Start Month} may be changed at any time without negatively\n    impacting your budget goals.\n\n    Regardless of the \\textbf{Start Month} selected, jGnash will use a rolling 12 month period based upon the \\textbf{Start Month} you\n    have selected.\n       \n    \\begin{figure}[h]\n        \\caption{Budget Properties}\n        \\includegraphics[width=0.45\\linewidth]{images/budget-properties}\n    \\end{figure}\n\n    The rounding method and number of decimals used to report budget results may be changed.\n    The default Rounding Mode is \\textit{Floor} which will round up expenses and round down income values which is a conservative\n    view of current income and expenses.\n\n    \\begin{table}[H]\n        \\begin{tabular}{|l|l|}\n            \\hline\n            \\textbf{Mode} & \\textbf{Description} \\\\\n            \\hline\n            \\hline\n            Ceiling & Rounds towards positive infinity \\textit{(Optimistic View)} \\\\\n            \\hline\n            Down & Rounds towards zero \\\\\n            \\hline\n            Half Down & Rounds towards nearest neighbor or down if equal distance \\\\\n            \\hline\n            Half Even & Rounds towards nearest neighbor or towards even if equal distance \\\\\n            \\hline\n            Half Up & Rounds towards nearest neighbor or up if equal distance \\textit{(Banking)} \\\\\n            \\hline\n            Floor & Rounds towards negative infinity \\textit{(Conservative View)} \\\\\n            \\hline\n            Up & Rounds away from zero \\\\\n            \\hline\n        \\end{tabular}\n        \\caption{Rounding Modes}\n    \\end{table}\n\n    Reported values are rounded based on the selected value for \\textbf{Scale}. Negative values for \\textbf{Scale} will round to the\n    left of the decimal point while positive values round to the right of the decimal point.\n\n    The upper limit of the \\textbf{Scale} option is limited by the largest \\textbf{Scale} value of the Currencies you have configured.\n    \\newpage\n    \\subsection{Budget Management}\n    Double clicking on an account name to the left of the panel will display a dialog that allows you to change the account\n    specific budget period and period amounts.\n\n    \\includegraphics[width=0.8\\linewidth]{images/budget-goal-dialog}\n\n    The Smart Fill panel may used to enter repeating patterns or fill in the amounts automatically based on the last 12 months.\n    Alternatively, you may directly edit the amount of each period by clicking and typing in a table cell.\n\n    The per account budget amounts as well as the \\textbf{Change} and \\textbf{Remaining} values are hierarchical in that the values of the child account\n    are summed and are added to the parent account. If a parent account is not configured has a placeholder, it may also be assigned\n    period goals that are inclusive of any children.\n\n    At the bottom of each reported budget period, a summary by account group is displayed.\n    To the right, a summary by account is displayed.\n    The summary's made be disabled if desired by unselecting the appropriate check boxes.\n\n    The \\menu{Export Spreadsheet} button will export a file to your choice of an \\texttt{.xls} or \\texttt{.xlsx} file.\n    The exported spreadsheet does contain formulas which makes it easier to manipulate the file externally.\n\n    \\subsection{Budgeting Tips}\n\n    When planing a budget, you need to consider how you spend and receive your money versus how you want to report your budget.\n\n    jGnash has to make assumptions when entering per account period amounts.\n    Internally, jGnash is keeping a list of 366 days (365 + 1 leap day) per account with the list starting at the first\n    calendar day of the year.\n\n    When a period goal for an account is entered, the amount is averaged across each day of the period.\n    Entry of amounts is also sensitive to the current year.\n    If you select Monthly for the account period, the monthly boundary for days is established by the current year calendar\n    months and the amount is then averaged across the number of days per each month.\n\n    Averaging of periods has an impact on how exact the tracking of your budget is.\n    If you choose to enter a monthly average for income, but are paid on certain days on the month, your budget will show\n    slight variations through the year.\n\n    If you want the budgeted vs.\\ Remaining amounts to be exact for a particular account, then you will want to set the\n    account period to be Daily and take the effort to enter your daily amount goals.\n\n    Minor discrepancies can occur during leap years due to the extra day. Budgets are typically dynamic due to continuous\n    changes in income and expenses which means you will naturally address these small discrepancies as you maintain your budget\n\n    You will not be able to export a spreadsheet when the report period is daily due to memory requirements and limitations\n    of some spreadsheet applications.\n\n    \\chapter{Reconciliation}\n    Reconciliation is a simple and visual process of matching up the transactions listed in an account's register against a\n    paper or electronic statement provided by a financial institution.\n    If differences do exist, then any missing or erroneous transactions must be addressed until the differences are resolved.\n    Statements should come from you bank, investment broker, credit card issuer, etc.\\ on a periodic basis.\n\n    Periodically reconciling an account helps ensure transaction entry errors do not creep in over time.\n\n    \\tipbox{\n    Reconciliation is also a great tool for monitoring your accounts for fraudulent transactions.\n    }\n\n    Accounts may be reconciled using a manual process or using the Reconciliation Wizard.\n    For any given reconciliation period, using a combination of both methods will be difficult.\n\n    Regardless of the Reconciliation process used, it is good practice to reconcile all accounts periodically.\n\n    \\section{Basics}\n    Every transaction entry will have two independent reconciliation states that applies to both of the related\n    crediting and debiting accounts.\n    Split transactions may have even more reconciliation states depending on how many accounts it touches.\n    Reconciliation states are explained a bit later in this chapter.\n\n    The default assumed reconciliation states can be configured depending on your preferred method of reconciliation.\n    Taking time to understand these options is important for a successful reconciliation process.\n\n    \\tipbox{\n    If this is your first time reconciling an account and you have prior transaction history with a mix of\n    Reconciled and Cleared transactions, you may need to manually reconcile prior transaction history.\n    }\n\n    \\subsection{Reconcile Settings}\n\n    jGnash makes it easier to manage reconciliation by providing some options described below.\n    These can be access in the \\textbf{Register} options tabs using the \\menu{Tools > Options} menu.\n\n    \\includegraphics[width=0.8\\linewidth]{images/reconcile-options}\n\n    Unless you have very specific needs, it is recommend that you choose to have the option \\textbf{All transaction accounts have\n    same reconciled state} or \\textbf{Automatically reconcile Income and Expense Accounts} selected as the default.\n\n    Selecting \\textbf{Automatically reconcile Income and Expense Accounts} requires a bit more work on your part in that you are\n    required to reconcile all institution statements, but it will not create any issues when transferring between accounts.\n    \\textit{This is the recommended option if you are using the Reconciliation Wizard.}\n\n    Selecting \\textbf{All transaction accounts have same reconciled state} will reduce the effort of reconciling transactions,\n    but can create problems reconciling transfers between bank accounts later. If you manually reconcile your accounts and\n    do not use the Reconciliation Wizard, this option saves a significant amount of work at the risk of making an assuming\n    a transaction occurred correctly between two institutions.\n    \\textit{Use of this option in conjunction with use of the Reconciliation Wizard can create problems with bank transfers when\n    you reconcile both accounts.}\n\n    Choosing to disable automatic reconciliation will require you to reconcile Income and Expense accounts for which you\n    may not have been provided a reconciliation statement.\n\n    \\subsection{Reconcile States}\n\n    jGnash transactions have three reconciliation states that are presented in order below:\n\n    \\begin{description}[style=nextline]\n        \\item[Not Reconciled]\n        The transaction has not been cleared or reconciled.\n        \\item[Cleared]\n        The transaction has been marked by the user to have been cleared during a manual or unfinished reconciliation process.\n        A transaction may be marked as cleared to draw attention to it without impairing use of the reconciliation wizard.\n        \\item[Reconciled]\n        The transaction has been automatically or manually reconciled.\n    \\end{description}\n\n    \\warningbox{\n    Manually marking transactions is not recommended if you are going to use the Reconcile Wizard.\n    }\n\n    \\section{Manual Reconciliation}\n    Manual reconciliation is the process of individually comparing the account register against the institution provided\n    statement and marking the matching transactions as reconciled.\n\n    The downside to manual reconciliation is not all checks and balances are performed against the reported opening and\n    closing balance for a given period. This increases the likely-hood of missing a recorded transaction or incorrectly\n    entered amount.\n\n    To manually mark a transaction as reconciled, use the context menu in the register to display options to change the\n    reconciled state.\n\n    \\includegraphics[width=1.0\\linewidth]{images/manual-reconcile-context}\n\n    \\begin{mdframed}[style=info]\n        Use of the context menu is currently the only means of marking a transaction as reconciled other than using the\n        reconciliation tool. It may also be used to clear transaction erroneously marked as reconciled.\n    \\end{mdframed}\n\n    Transactions may also be marked as \\textbf{Cleared }through the transaction form.\n    Some users may prefer to clear certain transactions manually during a given period to draw attention to them.\n    \\textbf{Cleared} transactions will still be visible within the Reconciliation Wizard if used later while manually\n    \\textbf{Reconciled} transactions will not.\n\n    \\includegraphics[width=1.0\\linewidth]{images/transaction-form}\n\n    \\section{Reconciliation Wizard}\n    Use of the Reconciliation Wizard helps to simplify the reconciliation process by comparing opening and closing balances\n    reported by the institution against the sum of the transactions as you mark them as reconciled. You receive\n    instantaneous visual feedback as you mark transactions, and at the end of the process you should have a net difference\n    of zero.\n\n    \\tipbox{\n    The Reconcile Wizard has a nice feature that is not immediately obvious. While the wizard is displayed, you can still\n    go back to the account register and enter missing transactions, correct erroneous amounts, or modify and delete\n    transactions if entered into the wrong account. The Wizard's credit and debit lists are fully dynamic.\n    You are not required to exit the Wizard without completing the process if you discover missing transactions or errors.\n    }\n\n    he image of the account register shown next is representative of a small but typical reconciliation period.\n    The amounts and balances shown correspond with the other images as the Reconcile Wizard is explained in this chapter.\n    Refer back to this image as necessary for clarification.\n\n    Take note of the \\texttt{Reconciled Balance:\\$2,614.43} and that it is the last transaction marked as reconciled.\n    Also, take note of the transaction dated \\texttt{03/25/14} and the corresponding balance of \\texttt{\\$1,369.70}.\n    You will see these same values later in the Reconcile Settings dialog.\n\n    \\includegraphics[width=1.0\\linewidth]{images/reconcile-register}\n\n    The Reconcile Wizard is started by using the context menu in the Account List, or by clicking the \\menu{Reconcile}\n    button in the transaction register.\n\n    A small dialog will be shown requesting some information.\n\n    \\begin{description}[style=nextline]\n        \\item[Statement Date]\n        This is the closing date for the reconciliation period.\n        This should be reported on your account statement.\n        The date will typically be the end of the month, but may be different due to institution or locale rules.\n        Transactions entered after this date will not appear within the Reconciliation Wizard\n        \\item[Opening Balance]\n        The opening balance should also be provided by your institution and should be equal to the closing balance\n        of the last reconciliation period.\n        In your account register, this will also be the account balance of the last reconciled transaction.\n        \\item[Ending Balance]\n        This amount should also be provided by you institution.\n    \\end{description}\n\n    \\textit{These values should be provided to you by your banking institution in paper or electronic format and\n    it's important these values are entered correctly, otherwise balances will not zero out.}\n\n    Pay special attention to the account type, the selected option for \\textit{Reverse Displayed Account Balances}\n    and if you are entering a positive or negative opening and ending balance.\n\n    \\includegraphics[width=0.4\\linewidth]{images/reconcile-settings}\n\n    After clicking the \\menu{OK} button, the settings dialog will be replaced by a\n    dialog showing all of the transactions prior and inclusive of the statement date\n    that have not been marked as Reconciled. \\textit{Transactions marked as cleared will be shown.}\n\n    The next step is to go through the institution provided statement and mark every\n    matching transaction as reconcilable by clicking on the transactions. As you\n    click each transaction, totals will update and you should see the\n    \\textbf{Difference} value approach zero. The symbol in the \\textit{Clr} column will\n    also change when marked as \\textbf{Reconciled}. When the \\textbf{Difference} is zero,\n    the \\menu{Finish} button will become active. Transactions marked as cleared will\n    also need to be selected if they are to be reconciled.\n\n    It is not unusual to find transactions that go unmarked for reconciliation near\n    the end of the statement period. These are transactions you have entered that\n    were not processed through the system fast enough to show up on your statement\n    and impact your account balance. Simply ignore these transactions and they will\n    be captured at the start of the next statement and reconciliation cycle.\n\n    \\newpage\n    \\includegraphics[width=0.8\\linewidth]{images/reconcile-dialog}\n\n    Clicking on the \\menu{Finish} button will close the dialog and will mark the selected transactions as Reconciled.\n    Depending on the number of transactions and type of file format being used, it could take awhile for the changes\n    to be saved.\n    A wait message will be displayed during the change process.\n\n    What do you do if you have marked all transactions as reconciled and the difference is not zero?\n\n    \\begin{itemize}\n        \\item Not all paper and electronic statements clearly identify fees, earned\n        interest, etc.\\ Make sure you have captured these transactions.\n        \\item Were any transactions amounts entered incorrectly?\n        \\item Transactions manually marked as Reconciled during the statement period will\n        not show in the transaction columns and are guaranteed to throw off balances.\n        Mark the transactions as \\textbf{Cleared} instead.\n        \\item Do you have your Reconcile Settings configured appropriately for the process\n        you are using? If in doubt, use \\textbf{Automatically reconcile Income and Expense Accounts}.\n        \\item Incorrectly entered opening and ending balances will cause errors in calculated balances.\n    \\end{itemize}\n\n    If you need to to exit the Reconciliation Wizard before finishing, the \\menu{Finish Later} button may be used.\n    This will close the dialog and mark selected transactions as \\textbf{Cleared}.\n    This makes it easy to restart the process with transaction you have already marked as reconcilable identified.\n    You will still need to re-select those transactions when you restart the process.\n\n    \\chapter{Securities}\n    \\label{ch:securities}\n    Securities must be managed when entering Investment Transactions within jGnash.  \n                    \n    At a minimum, a Security is assigned a unique Symbol, a Reported Currency and a Scale \\textit{(the number of decimal places \n    for entering and reporting prices)}.  \n\n    Tracking of prices and historical information for the Security is optional and not required to enter\n    investment transactions.  However, the reported \\textbf{Market Balance} of an account will more accurate if prices are entered.\n    \n    The same Security may be used for multiple accounts.\n    \n    \\tipbox{\n        The term Securities is used within jGnash generically for Stocks, Mutual Funds, Bonds and when trading commidities\n        such as gold and oil.\n    }\n    \n    \n    \\section{Create and Modify Securities}\n    \n    Securities may be created or modified using \\menu{Tools > Securities > Create / Modify}.  All of the properties are\n    accessible when modifying an existing Security.\n    \n    To create a new Security, simply fill out the form with the required properties and click the \\keys{Apply} button to save.\n    \n    You can modify an existing Security by selecting it in the list to the left and click \n    the \\keys{Apply} button to save the changes.\n    \n    \\begin{figure}[h]\n        \\caption{Create / Modify Securities}\n        \\includegraphics[width=0.6\\linewidth]{images/createModifySecurities}\n    \\end{figure}\n\n    \\begin{description}[style=nextline]\n        \\item[Symbol \\textit{(required)}]\n        The Symbol is typically the Ticker symbol assigned to the Security.  Depending on the Quote Source used,\n        the Symbol my be appended with a market or region code. This value may be left empty.\n        \\item[CUSIP / ISIN]\n        A internationally unique identifier for a Security.  Some Quote Sources may require use of this field\n        instead of using the Symbol.  At this time, it is not required. \n        \\item[Quote Source \\textit{(required)}]\n        The Quote Source is the online data source chosen to download prices and events.  This is discussed later \n        in the chapter.\n        \\item[Description]\n        A general description for the Security.  This value may be left empty.\n        \\item[Scale \\textit{(required)}]\n        This is the number of decimal places used for entry of security prices.  Mutual funds will typically\n        be traded at fractional prices.  This allows you to override the number of decimal places used by the\n        Reported Currency.\n        \\item[Reported Currency \\textit{(required)}]\n        Sometimes called the Base Currency, this is the currency the Security is being traded with.        \n    \\end{description}        \n    \n    \\subsection{Quote Sources}\n       \n    jGnash currently supports use of Yahoo and IEX Cloud as online data providers.  \n    \n    \\subsubsection{None}\n    The default quote source is \\textbf{None}.  If used, all pricing, stock splits and dividends will\n    need to be entered manually.\n    \n    \\subsubsection{IEX Cloud}\n    Using IEX Cloud as a Data Provider requires opening an account which may be free for use or a paid\n    subscription depending on the amount of data being consumed. \n    \n    An account can be created at \\href{https://iexcloud.io/}{IEX Cloud} \\texttt{[https://iexcloud.io/]}.\n    After creating your account, the \\textit{Secret Key} provided by IEX Cloud must be entered into the \n    Data Providers Options Tab as shown in Figure~\\ref{fig:dataproviders}.\n    \n    The \\textit{Secret Key} is stored within your jGnash file.  If the key is not entered,\n    jGnash will report an error when attempting to download information.\n    \n    \\begin{figure}[h]\n        \\caption{Data Providers}\n        \\label{fig:dataproviders}\n        \\includegraphics[width=0.6\\linewidth]{images/dataProvidersTab}\n    \\end{figure}\n    \n    \\subsubsection{Yahoo}\n    Prices and historical events are downloaded from \\href{https://finance.yahoo.com/}{Yahoo Finance} \\texttt{[https://finance.yahoo.com/]}.\n    Usage is subject to the conditions and terms of the Yahoo website.\n    \n    Yahoo does not make all Securities available, and it is not uncommon to experience periods of inaccessibility.\n    \n    \\subsection{Security Price History}\n    \n    Securities history is managed using \\menu{Tools > Securities > History}.\n    \n    The \\textbf{Security} of interest is selected at the top of the form.\n    A chart is displayed using on the historical closing price.  \n    As shown in Figure~\\ref{fig:securitieshistorydialog}, periods divided by a stock split\n    are shown using a different color.\n    \n     \\begin{figure}[h]                          \n        \\caption{Modify Securities History}                  \n        \\label{fig:securitieshistorydialog}   \n        \\includegraphics[width=1.0\\linewidth]{images/modifySecurityHistory}\n    \\end{figure}\n    \n    \\subsubsection{Price History Panel}\n        \n    The closing price as well as the daily high, low and trade volume can be filled in and added by clicking\n    on the \\keys{Add} button.  The \\keys{Clear} button will reset the entry form.  Selecting a date in the list\n    and clicking the \\keys{Delete} button will permanently remove the entry.\n    \n    At the bottom of the \\textbf{Price History Panel} are some convenience buttons to help pare down the amount of data\n    that can accumulate over time.  \n    \n    Thinning out the data can significantly reduce the size of your file and improve application performance.\n    \n    Please see \\hyperref[sec:pricelookup]{Price Lookup and Reporting} Section for specifics of how jGnash uses\n    the Price History for reporting and estimating Investment Account balances.\n    \n    \\subsubsection{Event History Panel}\n    \n    Similar to the \\textbf{Price History Panel}, Dividends and Splits may be entered.\n    \n    Dividends can be added for informational purposes but are not required.\n    \n    Split entries help to correctly manage the visual trend of the chart.  They are not used or required\n    to correctly calculate the value of your Investment Account.\n    \n    \\warningbox{\n       When Splits or Diviends do occur, it's important\n       to remember to create a Split Transaction within the Investment Account register.  \n       Splits and Dividend chart events do not automatically register has transaction events.\n    }\n    \n    \\tipbox{\n        When entering splits, the value is typicaly greater than 1.  For example, a 2:1 split is recorded as 2.0.\n        However, if a 1:2 split or similar occurs (known as a reverse split), a value of 0.5 would be entered.\n    }\n        \n           \n    \\subsection{Security Price Historical Download}\n    \n    Historical price information for Securities may be downloaded using \\menu{Tools > Securities > Historical Import}.\n    \n    Shown below Figure~\\ref{fig:historicalImport}, you need to specify the start and end dates as well as check the boxes of the Securities you\n    want to download the historical information for.\n    \n    Click the \\keys{Start} button at the bottom of the form.  The download and import process can take awhile \n    depending on the file type you are using.  \n    \n    If desired, you can click the \\keys{Stop} button to interrupt the process.  Interrupting the process will not\n    undo what has already been downloaded and imported prior to stopping.\n    \n    \\begin{figure}[h]\n        \\caption{Historical Import}\n        \\label{fig:historicalImport}\n        \\includegraphics[width=0.6\\linewidth]{images/securityHistoryImport}\n    \\end{figure}\n       \n    \\newpage   \n    \\subsection{Price Lookup and Reporting}\n    \\label{sec:pricelookup}\n           \n    When reporting balances for \\hyperref[sub:investaccount]{Investment Accounts}, jGnash will perform a search\n    for the most recent or closet price.\n        \n    \\begin{itemize}\n        \\item If an Investment Transaction occurs on the same day as a price record, the Transaction price will be used.\n        \\item For reporting periods such as Months or Quarters, the closest or exact match to the end of the period is used.\n        \\item  In all cases, the latest price if it's a Transaction or a Historical record is used for the last period \n        of a Report and as well as the reported \\textbf{Market Value} of the account shown in the Register.      \n    \\end{itemize}\n      \n    Deleting price records near the end of a reporting period can skew the reported interim values.\n\n    \\chapter{Importing Transactions}\n    Importing of OFX, QFX, MT940, and QIF transaction files is supported within jGnash.\n    The files are downloaded manually from your financial institution and then imported.\n    jGnash does not currently support an automatic download process.\n\n    QIF is an old format that was not well defined and is generated inconsistently between financial institutions.\n    There are two significantly different formats with one consisting of transnational data only and the other format\n    attempts to serve as an export / import format with account information for financial applications.\n    Use of date formats within the QIF format will vary and can lead to incorrect or failed import of transactions.\n    If at all possible, avoid use of QIF files and use an OFX or QFX file.\n\n    OFX and QFX files are a well defined standard and jGnash will easily process these.\n    This is the preferred file format for importing transactions into jGnash.\n    OFX and QFX files are XML based and human readable in that you can open the file\n    with a text editor and easily understand the information contained within.\n\n    MT940 is another well defined standard commonly used between financial institutions.\n    The format consists of fields separated by colons and commas with well defined numeric codes.\n    MT940 is very compact and efficient to transfer electronically.\n\n    \\notebox{\n    The Import Process below describes the basic import of transactions.\n    jGnash can also process OFX and QFX files with Investment transactions.\n    Additional columns will be present and enabled when applicable for these advanced types of transactions.\n    }\n\n    \\section{Import Process}\n    Importing transactions is an easy process that is guided with a simple wizard.\n    It begins with \\menu{File > Import > OFX / QFX}, \\menu{File > Import > QIF}, or\n    \\menu{File > Import > MT940} and selecting a file that has been downloaded.\n\n    The first step of the import process requires selecting the correct account the\n    import file is for. This is the base account all imported transactions will be\n    associated with.\n\n    \\tipbox{\n    When importing OFX or QFX files, jGnash will preselect the correct account after\n    it has learned from the first import.\n    }\n\n    The second step of the process is reviewing the transactions as shown below.\n\n    \\includegraphics[width=0.8\\linewidth]{images/importWizard2}\n\n    The first column consists of import status symbols:\n\n    \\begin{itemize}\n        \\item A plus symbol indicates the transaction has been detected as new. Double\n        clicking on it will change it to a negative symbol to indicate you do not want\n        it to be imported.\n        \\item An equals symbol indicates the transaction has been detected as a duplicate of\n        a manually entered transaction or already imported transaction. Double\n        clicking on it will change it to a plus symbol to indicate you want to force\n        the import of the transaction.\n    \\end{itemize}\n\n    The second, third, and forth columns are not editable. The Payee and Memo\n    columns may be pre-processed using JavaScript Filters as described later.\n\n    The Account column can be double clicked and changed to suit. The selected\n    account will normally be an income or expense account unless it was a transfer\n    to another bank account or financial institution. This column is also the\n    equivalent to \\textit{Categories} for some other financial applications.\n\n    The Amount column can be double clicked and edited. This is normally not\n    needed and not recommended, but allowed for some specialized uses when working\n    with multiple currencies.\n\n    The \\menu{Delete} button may also be used to remove a transaction being imported.\n\n    \\tipbox{\n    When importing transactions, jGnash uses what is called a Naive Bayes Classifier\n    to help preselect the best account for the transaction. Consistent use of\n    memo and payee fields will help accuracy of account selection. Accuracy also\n    improves with increased quantity of transactions over time.\n    }\n\n    The last step of the Import Wizard allows you to review basic information about\n    the import before committing to the changes. After clicking on \\menu{Finish},\n    the dialog will close and jGnash will begin the import process.\n\n    \\section{OFX imports}\n    Taking the time to correctly configure your jGnash Account properties can help\n    reduce the effort needed to automatically identify Accounts when importing OFX\n    transactions.\n\n    OFX files may contain enough information to identify transfers between accounts.\n    Most financial institutions will correctly identify transactions between checking\n    and savings accounts with the bank, but others may include enough information\n    when transferring between finance institutions.\n\n    Below is a fragment of the OFX file content defining the transfer. jGnash will\n    attempt to match the \\texttt{<ACCTID>} identifier on line 4 to an existing Account Number\n    that is specified within the properties of your jGnash account.\n    \\\\ % new line\n    \\begin{lstlisting}[caption={OFX File Fragment}]\n        <BANKACCTTO>\n        <BANKID>100000009\n        <BRANCHID>TEST\n        <ACCTID>555555-C01\n        <ACCTTYPE>CHECKING\n        </BANKACCTTO>\n    \\end{lstlisting}\n\n    The \\textbf{Account Number} is easily changed by modifying the Account properties as described\n    in the \\hyperref[subsec:creatingAccounts]{Creating Accounts} section of the manual.\n\n    \\section{JavaScript Filters}\n    When importing transactions from OFX/QFX, mt940, and QIF bank statements\n    downloads, transactions may be preprocessed using custom JavaScript files. As\n    of jGnash release 2.32.0, the scripts may be used to alter the memo or payee of\n    the transactions being imported.\n\n    You have three options for making a JavaScript file available for use:\n    \\begin{itemize}\n        \\item Some scripts are included by default. These show up as \\texttt{/jgnash/imports/xxx.js}\n        in the \\textbf{Script} column of the table.\n        \\item Place the JavaScript file into a \\directory{importScripts} sub directory where\n        your jGnash data file is installed.\n        \\item Place the \\texttt{.js} file into your home directly where jGnash will find it.\n        \\begin{itemize}\n            \\item \\directory{\\$HOME/.jgnash/importScripts} for UNIX and BSD based operating systems\n            \\item \\directory{USER\\_HOME\\textbackslash AppData\\textbackslash Local\\textbackslash jgnash\\textbackslash importScripts} for Windows operating systems.\n        \\end{itemize}\n    \\end{itemize}\n\n    These import scripts must be specifically enabled for use and the order in which they will be executed can be changed\n    using the \\menu{Tools > Configure Transaction Imports Filters…} menu as shown below.\n\n    \\includegraphics[width=0.9\\linewidth]{images/importFilters}\n\n    \\newpage\n    Below is an example JavaScript file. The first four functions are required for correct operation.\n\n    The first function accepts an \\textbf{ImportTransaction} for advanced manipulation of the imported data.\n\n    The second and third functions accept a \\textbf{string} and are expected to return a \\textbf{string}.\n\n    The \\textbf{getDescription} function should return a meaningful description of the script.\n    \\\\ % new line\n    \\begin{lstlisting}[caption={Example Import Script},language=JavaScript,numbers=left]\n        /* Normalizes the case of the payee and memo fields */\n\n        // place holder for the passed ImportTransaction\n        var importTransaction;\n\n        // This will be called first and passed the ImportTransaction\n        function acceptTransaction(transaction) {\n\n            // does nothing with it in this script\n            importTransaction = transaction;\n        }\n\n        // This is a required function\n        function processMemo(memo) {\n            return capitalizeFirstLetter(memo.toLocaleLowerCase());\n        }\n\n        // This is a required function\n        function processPayee(payee) {\n            return titleCase(payee.toLocaleLowerCase());\n        }\n\n        // This is a required function\n        var getDescription = function (locale) {\n            var Locale = Packages.java.util.Locale;\n    \n            switch (locale) {\n                case Locale.ENGLISH:\n                    return \"Tidy Memo and Payee fields\";\n                default:\n                    return \"Tidy Memo and Payee fields\";\n            }\n        };\n\n        // Capitalizes the first letter or a String\n        function capitalizeFirstLetter(str) {\n            return str.charAt(0).toUpperCase() + str.slice(1);\n        }\n\n        // Converts a string to Title Case\n        function titleCase(str) {\n            return str.replace(/(^|\\s)[a-z]/g,function(f){return f.toUpperCase();});\n        }\n    \\end{lstlisting}\n\n    The full jGnash API may be accessed within these scripts for advanced processing capabilities.\n    See the \\hyperref[sec:javascript]{JavaScript} section of the manual for more details.\n\n    \\notebox{\n    JavaScript files are expected to be encoded as UTF-8 Files.\n    An incorrect file encoding may cause script failures.\n    }\n\n    \\chapter{Reports}\n    A variety of reports exist that present your financial history and status in different ways.\n    There are currently three classes of reports available.\n    Text reports can be exported and easily imported into a spreadsheet for advanced manipulation.\n    Chart based reports may be altered and exported to a graphic file or printed using the context sensitive pop-up menu.\n    Tabular type reports may be printed or saved as \\textit{PDF}, \\textit{XLS}, or \\textit{XLSX} files.\n\n    \\section{Tabular Reports}\n    Tabular reports are displayed in a viewer that allows you to change the page and print or export the report.\n    The font size of the displayed report can be changed from the toolbar of the report window.\n\n    The fonts used to display the report may be changed in the \\menu{Tools > Options}\n    dialog shown below. The \\textbf{Proportional} font is typically used for report headers and footers.\n    The \\textbf{ Monospace} font, also called a fixed-width font, is used to display the reported values.\n\n    \\includegraphics[width=0.6\\linewidth]{images/font-options.png}\n\n    If a proportional spaced font is chosen for the \\textbf{ Monospace} font, numeric report values may not line up\n    correctly in the report.\n\n    \\tipbox{\n    Information on font types as well as a wide selection of freely available fonts can be found on the Internet.\n    Once a new font is properly installed in your operating system, it will be available for use in jGnash the next\n    time it is started.\n    }\n\n    \\section{Tips}\n    Depending on your operating system, or locale you may need to change the font type and font size to achieve\n    the best looking report.\n    The font size can be changed on the report toolbar and is remember for each report type.\n\n    \\subsection{Nothing displays in the report and I'm not getting any errors}\n    Try increasing your font size, and if that does not work, choose a different font.\n    Depending on your operation system, fonts may not render correctly at reduced sizes.\n\n    \\subsection{I get an error that tells me to reduce my font size}\n    The selected font size is too large to display the report correctly.\n    You will need to choose a smaller font size.\n    Many times, the column heading text may dictate the displayed width of a column.\n    Try choosing a proportional font with condensed spacing.\n    You may also want to check the default paper size and adjust if needed.\n\n    \\subsection{My PDF exports are missing information or don't look correct on different computers}\n    Not all fonts are able to be embedded within a \\texttt{PDF} file.\n    You may need to experiment with different fonts to achieve good portability.\n    In most cases, the defaults jGnash chooses will give you good results.\n\n    \\subsection{The IRR is not being displayed in my Portfolio report}\n    Your investment account may not be setup properly. Ensure that stocks have been added\n    or purchased prior to sells, buys, dividends, etc.\n\n    \\chapter{Administration}\n    Several administration options and tools are provided to help with management of your data.\n\n    \\section{File | Save As}\n    An open file may be saved as a new file of the same type, or a new file with a new file type.\n    To save the file as a different type, you must change the file extension to a supported type.\n    Correct file extensions are shown below.\n\n    \\begin{table}[H]\n        \\begin{tabular}{|l|l|}\n            \\hline\n            XML File & .xml \\\\\n            \\hline\n            Binary File & .bxds \\\\\n            \\hline\n            H2 Relational Database (B-Tree) & .h2.db \\\\\n            \\hline\n            H2 Relational Database (MVStore) & .mv.db \\textit{(newest H2 format and is more compact)} \\\\\n            \\hline\n            HyperSql (hslqb) Relational Database & .script \\textit{(.lobs, .log, .properties are used as support files)} \\\\\n            \\hline\n        \\end{tabular}\n        \\caption{File Types}\n    \\end{table}\n\n    Depending on the file type, jGnash may generate an intermediate file for the conversion process.\n    When saving to a relational database, the process may take awhile to complete.\n\n    \\section{File | Export | Export Accounts and File | Import | Import Accounts}\n    Use of these commands allows you to export your account structure and import it back into and new file.\n    This is handy if you want to start a new file without manually recreating your accounts.\n    This does not preserve existing transactions.\n\n    \\section{Change Database Password}\n    By default, when a new relational database is created, a password is not specified. This allows you to password protect\n    your file. This does not encrypt your data, so a person with the right tools can \\underline{easily} access your data.\n    It is useful for casual protection only. If encryption is important, use OS level encryption capability available on\n    any modern operating system. This is disabled while a file is open.\n\n    \\section{Shutdown Server}\n    Issues a shutdown request to a remote server.\n    This is disabled while a file is open.\n\n\n    \\chapter{Plugins and JavaScript}\n    jGnash support the addition of JavaScript and Plugins to add additional functionality to the application.\n\n    \\section{Plugins}\n    Plugins are tightly integrated into jGnash, and once loaded, behave as if they are a standard part of the application.\n    Plugins are coded in Java using a jGnash specific API as the entry point so they may be loaded into jGnash.\n\n    Standard Plugins are packaged into \\texttt{JAR} files and are typically located within the \\directory{plugins}\n    directory located in the directory jGnash is installed.\n\n    You have two options for manually installing a Plugin:\n    \\begin{itemize}\n        \\item Place the JAR file into the \\directory{plugins} sub directory where jGnash is installed and restart jGnash.\n        \\item Place the JAR file into your home directly where jGnash will find it.\n        \\begin{itemize}\n            \\item \\directory{\\$HOME/.jgnash/plugins} for UNIX and BSD based operating systems.\n            \\item \\directory{USER\\_HOME\\textbackslash AppData\\textbackslash Local\\textbackslash jgnash\\textbackslash plugins} for Windows operating systems.\n        \\end{itemize}\n    \\end{itemize}\n\n    The jGnash JavaDoc may be referenced if you are interested in creating a jGnash Plugin.\n    The MT940 import is written as a standard Plugin and may be referenced as an example of how to write one.\n\n    \\section{JavaScript}\n    \\label{sec:javascript}\n\n    In addition to use of Plugins, jGnash allows you to create and run JavaScript programs.\n    The internals of the jGnash engine and some user interface functions can be accessed to create custom reports, create\n    and modify transactions, etc.\n\n    Running a JavaScript program is as simple as using \\menu{Tools > Run JavaScript} command from the menu bar.\n\n    Below is an example JavaScript program that displays the accounts in the currently loaded file and demonstrates how to\n    display a simple dialog.\n    To try the program, create a text file using your favorite editor with a name of your choice that ends with\n    a \\texttt{.js} extension.\n    After creating the file, simply using the \\menu{Tools > Run JavaScript} command to select the program and run it.\n    \\\\\n    \\begin{lstlisting}[language=JavaScript,numbers=left]\n        load(\"nashorn:mozilla_compat.js\"); // Load compatibility script\n\n        importPackage(javax.swing);\n        importPackage(Packages.jgnash.ui);\n        importPackage(Packages.jgnash.engine);\n\n        // helper function to print messages to the console\n        function debug(message) {\n            java.lang.System.out.println(message);\n        }\n\n        // show the console dialog to see the debug information\n        var Console = Java.type(\"jgnash.uifx.views.main.ConsoleDialogController\");\n        Console.show();\n\n        // this is how to get the default Engine instance\n        var engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        // get a list of accounts\n        var accountList = engine.getAccountList();\n\n        // loop and print the account names to the console\n        for (var i = 0; i; accountList.size(); i++)\n        {\n            var account = accountList.get(i);\n            debug(account.getName());\n        }\n    \\end{lstlisting}\n\n    \\notebox{\n    JavaScript files are expected to be encoded as UTF-8 Files. An incorrect file encoding may cause script failures.\n    }\n\n    JavaScript programs have the advantage of not requiring the use of an IDE or Java compiler to create and test a program.\n    The disadvantage is troubleshooting syntax and logic errors can be more difficult than writing a jGnash Plugin.\n\n    % ======================\n    % Command Line Options\n    % ======================\n    \\chapter{Command Line Options}\n    \\label{ch:cmdOptions}\n\n    jGnash has several command line options for advanced users.\n\n    Parameters such as file names that include a space in the path must be escaped using double quotes.\n\n    \\begin{mdframed}[style=info]\n        \\textbf{Example} \\\\ \\\\\n        The path to the file \"/home/craig/jgnash files/jgnash.h2.db\" must be escaped as shown.\n        \\\\ \\\\\n        \\texttt{jGnash -{}-server \"/home/craig/jgnash files/jgnash.h2.db\" -{}-password fh56dy}\n    \\end{mdframed}\n\n    \\section{Options}\n    \\begin{description}\n        \\item[-h, -{}-help]\n        Detailed display of all command line options.\n        \\item[-f, -{}-file \\textit{filename}]\n        Specifies a file to load at startup.\n        \\item[-p, -{}-portable]\n        If portable is specified on the command line, jGnash preferences will be stored to a file name \\texttt{pref.xml}\n        instead of using the system registry.\n        Use of this option is intended for users who want to run jGnash from a thumb drive on multiple computers and\n        maintain their preferences without using the system registry.\n        The \\texttt{pref.xml} file will usually be stored at the location jGnash was started from.\n        \\item[-{}-portableFile \\textit{filename}]\n        If you don't like the location the \\texttt{pref.xml} file is stored, or wish to use a different name, use\n        this option to change the location and name to suit.\n        \\item[-{}-bypassBootloader]\n        Bypasses the boot loader and requires manual installation of any OS specific files.\n        \\item[-u, -{}-uninstall]\n        Removes all registry and configuration settings jGnash has created.\n        This will not have any effect if you have been using the \\texttt{--portable} option.\n    \\end{description}\n\n    \\section{Client/Server Options}\n    \\begin{description}\n        \\item[-{}-server \\textit{filename}]\n        Starts the jGnash server using the specified file which must be must be a jGnash relational database.\n        The file must exist and not be in use by another program.\n        A user interface will not be displayed.\n        \\item[-{}-host \\textit{servername}]\n        Specifies the name of the remote server.\n        This starts jGnash and automatically connects to the specified server.\n        If running on the same computer as the server, \\texttt{localhost} may be used as the name of the server.\n        \\item[-{}-shutdown]\n        Issues a shutdown request to a server.\n        If -{}-host is not specified, then \\texttt{localhost} is assumed for the server name\n        \\item[-{}-password \\textit{password}]\n        The password that the client must correctly specify to connect to the jGnash database.\n        This is not required if the database is not protected.\n        A password does nothing to encrypt a file.\n        \\item[-{}-port \\textit{port}]\n        An empty port for network communications.\n        The specified port and \\texttt{port+1} may not be used by any other application at the same time.\n        The default port is 5300.\n    \\end{description}\n\n    \\notebox{\n    It is possible to start the jGnash client and specify the server, and password settings from\n    the \\menu{File > Open} dialog.\n    }\n    \\newpage\n    \\section{Client/Server Examples}\n    Start the jGnash server using the default port with a password protected database\n    \\begin{mdframed}[style=info]\n        \\texttt{jGnash -{}-server \"/home/craig/jgnash.mv.db\" -{}-password fh56dy}\n    \\end{mdframed}\n\n    Start the jGnash client and connect to the local server running a password protected database\n    \\begin{mdframed}[style=info]\n        \\texttt{jGnash -{}-host localhost -{}-password fh56dy}\n    \\end{mdframed}\n\n    Issue a shutdown request to a remote server that is password protected\n    \\begin{mdframed}[style=info]\n        \\texttt{jGnash -{}-shutdown -{}-host serv1 -{}-password fh56dy}\n    \\end{mdframed}\n\n\n    Issue a shutdown request to a local server that is not password protected\n    \\begin{mdframed}[style=info]\n        \\texttt{jGnash -{}-shutdown}\n    \\end{mdframed}\n\n    \\chapter{Frequently Asked Questions}\\label{ch:frequently-asked-questions}\n\n    \\begin{description}\n        \\item[Where do I set my opening account balance?]\n        Create an Equity Account called \"Opening Balances\" and transfer money from the Equity account to they\n        new account which will establish an opening balance.\n        The \"Opening Balances\" account may be hidden later if you do not want it visible.\n        \\item[What happened to transaction categories?]\n        Most commercial personal finance applications use categories to help track spending and income.\n        jGnash uses Income and Expense accounts instead of categories for tracking where your money\n        comes from and where it goes.\n        \\item[Can I use multiple currencies?]\n        Yes, the \\menu{Tool > Currencies > Add/Remove} menu will let you add additional currencies.\n        \\\\ \\\\\n        After adding new currencies, simple create new accounts that use the new currency.\n        When creating a transaction between accounts with different currencies, a field for the exchange rate will be enabled.\n        \\item[How do I add Securities / Stocks to my Investment and Mutual Fund Account?]\n        First, you need to have created your stocks/securities. \\menu{Tools > Commodities > Create / Modify}.\n        \\\\ \\\\\n        When creating the securities, the scale field must be filled in and the prefix field should be filled in.\n        The scale will generally be the same scale as the currency the securities value is reported in.\n        In most cases, a scale of 2 will work fine.\n        For the prefix, the currency prefix of the reported value should be used.\n        \\\\ \\\\\n        After creating your securities, you can go back and modify the existing account or select the securities when\n        creating a new account.\n        Use the \\menu{Securities} button in the dialog to make changes.\n    \\end{description}\n    \\begin{appendices}\n        \\chapter{Keyboard Shortcuts}\\label{ch:keyboard-shortcuts}\n        This Appendix contains application keyboard shortcuts that are available to you throughout jGnash\n\n        Depending on your operating system and how you have it configured, other shortcuts may be\n        available that perform the same function.\n        \n        \\begin{table}[H]\n            \\begin{tabular}{|l|l|}\n                \\hline\n                \\textbf{Keys} & \\textbf{Function} \\\\\n                \\hline\n                \\hline\n                \\keys{CTRL + F4}& Closes the active register window if you have one open \\\\\n                \\hline\n                \\keys{CTRL + A} & Displays the About dialog \\textit{(When a register table does not have the focus)}\\\\\n                \\hline\n                \\keys{CTRL + A} & Selects all transactions \\textit{(When a register table has the focus)}\\\\\n                \\hline\n                \\keys{CTRL + C} & Copies selected transaction information to the clipboard \\\\\n                \\hline\n            \\end{tabular}\n            \\caption{Shortcut Keys}\n        \\end{table}\n\n        \\begin{table}[H]\n            \\begin{tabular}{|l|l|}\n                \\hline\n                \\textbf{Keys} & \\textbf{Function} \\\\\n                \\hline\n                \\hline\n                \\keys{CTRL + C} & Copy \\\\\n                \\hline\n                \\keys{CTRL + X} & Cut \\\\\n                \\hline\n                \\keys{CTRL + V} & Paste \\\\\n                \\hline\n            \\end{tabular}\n            \\caption{Editing Keys}\n        \\end{table}\n\n        \\include{gpl-3.0}\n\n        \\include{fdl-1.3}\n        %\\chapter{GNU Free Documentation License}\n\n    \\end{appendices}\n\\end{document}\n"
  },
  {
    "path": "jgnash-manual/src/fdl-1.3.tex",
    "content": "\\chapter{GNU Free Documentation License}\n%\\phantomsection  % so hyperref creates bookmarks\n%\\addcontentsline{toc}{chapter}{GNU Free Documentation License}\n\n\n \\begin{center}\n       Version 1.3, 3 November 2008\n\n Copyright \\copyright{} 2000, 2001, 2002, 2007, 2008  Free Software Foundation, Inc.\n \n \\bigskip \n     \\texttt{<https://fsf.org/>}  \n \\bigskip\n \n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\\end{center}\n\n\n\\begin{center}\n{\\textbf{\\large} Preamble}\n\\end{center}\n\nThe purpose of this License is to make a manual, textbook, or other\nfunctional and useful document ``free'' in the sense of freedom: to\nassure everyone the effective freedom to copy and redistribute it,\nwith or without modifying it, either commercially or noncommercially.\nSecondarily, this License preserves for the author and publisher a way\nto get credit for their work, while not being considered responsible\nfor modifications made by others.\n\nThis License is a kind of ``copyleft'', which means that derivative\nworks of the document must themselves be free in the same sense.  It\ncomplements the GNU General Public License, which is a copyleft\nlicense designed for free software.\n\nWe have designed this License in order to use it for manuals for free\nsoftware, because free software needs free documentation: a free\nprogram should come with manuals providing the same freedoms that the\nsoftware does.  But this License is not limited to software manuals;\nit can be used for any textual work, regardless of subject matter or\nwhether it is published as a printed book.  We recommend this License\nprincipally for works whose purpose is instruction or reference.\n\n\n\\begin{center}\n{\\Large\\textbf{1. APPLICABILITY AND DEFINITIONS} \\par}\n\\end{center}\n\nThis License applies to any manual or other work, in any medium, that\ncontains a notice placed by the copyright holder saying it can be\ndistributed under the terms of this License.  Such a notice grants a\nworld-wide, royalty-free license, unlimited in duration, to use that\nwork under the conditions stated herein.  The ``\\textbf{Document}'', below,\nrefers to any such manual or work.  Any member of the public is a\nlicensee, and is addressed as ``\\textbf{you}''.  You accept the license if you\ncopy, modify or distribute the work in a way requiring permission\nunder copyright law.\n\nA ``\\textbf{Modified Version}'' of the Document means any work containing the\nDocument or a portion of it, either copied verbatim, or with\nmodifications and/or translated into another language.\n\nA ``\\textbf{Secondary Section}'' is a named appendix or a front-matter section of\nthe Document that deals exclusively with the relationship of the\npublishers or authors of the Document to the Document's overall subject\n(or to related matters) and contains nothing that could fall directly\nwithin that overall subject.  (Thus, if the Document is in part a\ntextbook of mathematics, a Secondary Section may not explain any\nmathematics.)  The relationship could be a matter of historical\nconnection with the subject or with related matters, or of legal,\ncommercial, philosophical, ethical or political position regarding\nthem.\n\nThe ``\\textbf{Invariant Sections}'' are certain Secondary Sections whose titles\nare designated, as being those of Invariant Sections, in the notice\nthat says that the Document is released under this License.  If a\nsection does not fit the above definition of Secondary then it is not\nallowed to be designated as Invariant.  The Document may contain zero\nInvariant Sections.  If the Document does not identify any Invariant\nSections then there are none.\n\nThe ``\\textbf{Cover Texts}'' are certain short passages of text that are listed,\nas Front-Cover Texts or Back-Cover Texts, in the notice that says that\nthe Document is released under this License.  A Front-Cover Text may\nbe at most 5 words, and a Back-Cover Text may be at most 25 words.\n\nA ``\\textbf{Transparent}'' copy of the Document means a machine-readable copy,\nrepresented in a format whose specification is available to the\ngeneral public, that is suitable for revising the document\nstraightforwardly with generic text editors or (for images composed of\npixels) generic paint programs or (for drawings) some widely available\ndrawing editor, and that is suitable for input to text formatters or\nfor automatic translation to a variety of formats suitable for input\nto text formatters.  A copy made in an otherwise Transparent file\nformat whose markup, or absence of markup, has been arranged to thwart\nor discourage subsequent modification by readers is not Transparent.\nAn image format is not Transparent if used for any substantial amount\nof text.  A copy that is not ``Transparent'' is called ``\\textbf{Opaque}''.\n\nExamples of suitable formats for Transparent copies include plain\nASCII without markup, Texinfo input format, LaTeX input format, SGML\nor XML using a publicly available DTD, and standard-conforming simple\nHTML, PostScript or PDF designed for human modification.  Examples of\ntransparent image formats include PNG, XCF and JPG.  Opaque formats\ninclude proprietary formats that can be read and edited only by\nproprietary word processors, SGML or XML for which the DTD and/or\nprocessing tools are not generally available, and the\nmachine-generated HTML, PostScript or PDF produced by some word\nprocessors for output purposes only.\n\nThe ``\\textbf{Title Page}'' means, for a printed book, the title page itself,\nplus such following pages as are needed to hold, legibly, the material\nthis License requires to appear in the title page.  For works in\nformats which do not have any title page as such, ``Title Page'' means\nthe text near the most prominent appearance of the work's title,\npreceding the beginning of the body of the text.\n\nThe ``\\textbf{publisher}'' means any person or entity that distributes\ncopies of the Document to the public.\n\nA section ``\\textbf{Entitled XYZ}'' means a named subunit of the Document whose\ntitle either is precisely XYZ or contains XYZ in parentheses following\ntext that translates XYZ in another language.  (Here XYZ stands for a\nspecific section name mentioned below, such as ``\\textbf{Acknowledgements}'',\n``\\textbf{Dedications}'', ``\\textbf{Endorsements}'', or ``\\textbf{History}''.)  \nTo ``\\textbf{Preserve the Title}''\nof such a section when you modify the Document means that it remains a\nsection ``Entitled XYZ'' according to this definition.\n\nThe Document may include Warranty Disclaimers next to the notice which\nstates that this License applies to the Document.  These Warranty\nDisclaimers are considered to be included by reference in this\nLicense, but only as regards disclaiming warranties: any other\nimplication that these Warranty Disclaimers may have is void and has\nno effect on the meaning of this License.\n\n\n\\begin{center}\n{\\Large\\textbf{2. VERBATIM COPYING} \\par}\n\\end{center}\n\nYou may copy and distribute the Document in any medium, either\ncommercially or noncommercially, provided that this License, the\ncopyright notices, and the license notice saying this License applies\nto the Document are reproduced in all copies, and that you add no other\nconditions whatsoever to those of this License.  You may not use\ntechnical measures to obstruct or control the reading or further\ncopying of the copies you make or distribute.  However, you may accept\ncompensation in exchange for copies.  If you distribute a large enough\nnumber of copies you must also follow the conditions in section~3.\n\nYou may also lend copies, under the same conditions stated above, and\nyou may publicly display copies.\n\n\n\\begin{center}\n{\\Large\\textbf{3. COPYING IN QUANTITY} \\par}\n\\end{center}\n\n\nIf you publish printed copies (or copies in media that commonly have\nprinted covers) of the Document, numbering more than 100, and the\nDocument's license notice requires Cover Texts, you must enclose the\ncopies in covers that carry, clearly and legibly, all these Cover\nTexts: Front-Cover Texts on the front cover, and Back-Cover Texts on\nthe back cover.  Both covers must also clearly and legibly identify\nyou as the publisher of these copies.  The front cover must present\nthe full title with all words of the title equally prominent and\nvisible.  You may add other material on the covers in addition.\nCopying with changes limited to the covers, as long as they preserve\nthe title of the Document and satisfy these conditions, can be treated\nas verbatim copying in other respects.\n\nIf the required texts for either cover are too voluminous to fit\nlegibly, you should put the first ones listed (as many as fit\nreasonably) on the actual cover, and continue the rest onto adjacent\npages.\n\nIf you publish or distribute Opaque copies of the Document numbering\nmore than 100, you must either include a machine-readable Transparent\ncopy along with each Opaque copy, or state in or with each Opaque copy\na computer-network location from which the general network-using\npublic has access to download using public-standard network protocols\na complete Transparent copy of the Document, free of added material.\nIf you use the latter option, you must take reasonably prudent steps,\nwhen you begin distribution of Opaque copies in quantity, to ensure\nthat this Transparent copy will remain thus accessible at the stated\nlocation until at least one year after the last time you distribute an\nOpaque copy (directly or through your agents or retailers) of that\nedition to the public.\n\nIt is requested, but not required, that you contact the authors of the\nDocument well before redistributing any large number of copies, to give\nthem a chance to provide you with an updated version of the Document.\n\n\n\\begin{center}\n{\\Large\\textbf{4. MODIFICATIONS} \\par}\n\\end{center}\n\nYou may copy and distribute a Modified Version of the Document under\nthe conditions of sections 2 and 3 above, provided that you release\nthe Modified Version under precisely this License, with the Modified\nVersion filling the role of the Document, thus licensing distribution\nand modification of the Modified Version to whoever possesses a copy\nof it.  In addition, you must do these things in the Modified Version:\n\n\\begin{itemize}\n\\item[A.] \n   Use in the Title Page (and on the covers, if any) a title distinct\n   from that of the Document, and from those of previous versions\n   (which should, if there were any, be listed in the History section\n   of the Document).  You may use the same title as a previous version\n   if the original publisher of that version gives permission.\n   \n\\item[B.]\n   List on the Title Page, as authors, one or more persons or entities\n   responsible for authorship of the modifications in the Modified\n   Version, together with at least five of the principal authors of the\n   Document (all of its principal authors, if it has fewer than five),\n   unless they release you from this requirement.\n   \n\\item[C.]\n   State on the Title page the name of the publisher of the\n   Modified Version, as the publisher.\n   \n\\item[D.]\n   Preserve all the copyright notices of the Document.\n   \n\\item[E.]\n   Add an appropriate copyright notice for your modifications\n   adjacent to the other copyright notices.\n   \n\\item[F.]\n   Include, immediately after the copyright notices, a license notice\n   giving the public permission to use the Modified Version under the\n   terms of this License, in the form shown in the Addendum below.\n   \n\\item[G.]\n   Preserve in that license notice the full lists of Invariant Sections\n   and required Cover Texts given in the Document's license notice.\n   \n\\item[H.]\n   Include an unaltered copy of this License.\n   \n\\item[I.]\n   Preserve the section Entitled ``History'', Preserve its Title, and add\n   to it an item stating at least the title, year, new authors, and\n   publisher of the Modified Version as given on the Title Page.  If\n   there is no section Entitled ``History'' in the Document, create one\n   stating the title, year, authors, and publisher of the Document as\n   given on its Title Page, then add an item describing the Modified\n   Version as stated in the previous sentence.\n   \n\\item[J.]\n   Preserve the network location, if any, given in the Document for\n   public access to a Transparent copy of the Document, and likewise\n   the network locations given in the Document for previous versions\n   it was based on.  These may be placed in the ``History'' section.\n   You may omit a network location for a work that was published at\n   least four years before the Document itself, or if the original\n   publisher of the version it refers to gives permission.\n   \n\\item[K.]\n   For any section Entitled ``Acknowledgements'' or ``Dedications'',\n   Preserve the Title of the section, and preserve in the section all\n   the substance and tone of each of the contributor acknowledgements\n   and/or dedications given therein.\n   \n\\item[L.]\n   Preserve all the Invariant Sections of the Document,\n   unaltered in their text and in their titles.  Section numbers\n   or the equivalent are not considered part of the section titles.\n   \n\\item[M.]\n   Delete any section Entitled ``Endorsements''.  Such a section\n   may not be included in the Modified Version.\n   \n\\item[N.]\n   Do not retitle any existing section to be Entitled ``Endorsements''\n   or to conflict in title with any Invariant Section.\n   \n\\item[O.]\n   Preserve any Warranty Disclaimers.\n\\end{itemize}\n\nIf the Modified Version includes new front-matter sections or\nappendices that qualify as Secondary Sections and contain no material\ncopied from the Document, you may at your option designate some or all\nof these sections as invariant.  To do this, add their titles to the\nlist of Invariant Sections in the Modified Version's license notice.\nThese titles must be distinct from any other section titles.\n\nYou may add a section Entitled ``Endorsements'', provided it contains\nnothing but endorsements of your Modified Version by various\nparties---for example, statements of peer review or that the text has\nbeen approved by an organization as the authoritative definition of a\nstandard.\n\nYou may add a passage of up to five words as a Front-Cover Text, and a\npassage of up to 25 words as a Back-Cover Text, to the end of the list\nof Cover Texts in the Modified Version.  Only one passage of\nFront-Cover Text and one of Back-Cover Text may be added by (or\nthrough arrangements made by) any one entity.  If the Document already\nincludes a cover text for the same cover, previously added by you or\nby arrangement made by the same entity you are acting on behalf of,\nyou may not add another; but you may replace the old one, on explicit\npermission from the previous publisher that added the old one.\n\nThe author(s) and publisher(s) of the Document do not by this License\ngive permission to use their names for publicity for or to assert or\nimply endorsement of any Modified Version.\n\n\n\\begin{center}\n{\\Large\\textbf{5. COMBINING DOCUMENTS} \\par}\n\\end{center}\n\n\nYou may combine the Document with other documents released under this\nLicense, under the terms defined in section~4 above for modified\nversions, provided that you include in the combination all of the\nInvariant Sections of all of the original documents, unmodified, and\nlist them all as Invariant Sections of your combined work in its\nlicense notice, and that you preserve all their Warranty Disclaimers.\n\nThe combined work need only contain one copy of this License, and\nmultiple identical Invariant Sections may be replaced with a single\ncopy.  If there are multiple Invariant Sections with the same name but\ndifferent contents, make the title of each such section unique by\nadding at the end of it, in parentheses, the name of the original\nauthor or publisher of that section if known, or else a unique number.\nMake the same adjustment to the section titles in the list of\nInvariant Sections in the license notice of the combined work.\n\nIn the combination, you must combine any sections Entitled ``History''\nin the various original documents, forming one section Entitled\n``History''; likewise combine any sections Entitled ``Acknowledgements'',\nand any sections Entitled ``Dedications''.  You must delete all sections\nEntitled ``Endorsements''.\n\n\\begin{center}\n{\\Large\\textbf{6. COLLECTIONS OF DOCUMENTS} \\par}\n\\end{center}\n\nYou may make a collection consisting of the Document and other documents\nreleased under this License, and replace the individual copies of this\nLicense in the various documents with a single copy that is included in\nthe collection, provided that you follow the rules of this License for\nverbatim copying of each of the documents in all other respects.\n\nYou may extract a single document from such a collection, and distribute\nit individually under this License, provided you insert a copy of this\nLicense into the extracted document, and follow this License in all\nother respects regarding verbatim copying of that document.\n\n\n\\begin{center}\n{\\Large\\textbf{7. AGGREGATION WITH INDEPENDENT WORKS} \\par}\n\\end{center}\n\n\nA compilation of the Document or its derivatives with other separate\nand independent documents or works, in or on a volume of a storage or\ndistribution medium, is called an ``aggregate'' if the copyright\nresulting from the compilation is not used to limit the legal rights\nof the compilation's users beyond what the individual works permit.\nWhen the Document is included in an aggregate, this License does not\napply to the other works in the aggregate which are not themselves\nderivative works of the Document.\n\nIf the Cover Text requirement of section~3 is applicable to these\ncopies of the Document, then if the Document is less than one half of\nthe entire aggregate, the Document's Cover Texts may be placed on\ncovers that bracket the Document within the aggregate, or the\nelectronic equivalent of covers if the Document is in electronic form.\nOtherwise they must appear on printed covers that bracket the whole\naggregate.\n\n\n\\begin{center}\n{\\Large\\textbf{8. TRANSLATION} \\par}\n\\end{center}\n\n\nTranslation is considered a kind of modification, so you may\ndistribute translations of the Document under the terms of section~4.\nReplacing Invariant Sections with translations requires special\npermission from their copyright holders, but you may include\ntranslations of some or all Invariant Sections in addition to the\noriginal versions of these Invariant Sections.  You may include a\ntranslation of this License, and all the license notices in the\nDocument, and any Warranty Disclaimers, provided that you also include\nthe original English version of this License and the original versions\nof those notices and disclaimers.  In case of a disagreement between\nthe translation and the original version of this License or a notice\nor disclaimer, the original version will prevail.\n\nIf a section in the Document is Entitled ``Acknowledgements'',\n``Dedications'', or ``History'', the requirement (section~4) to Preserve\nits Title (section~1) will typically require changing the actual\ntitle.\n\n\n\\begin{center}\n{\\Large\\textbf{9. TERMINATION} \\par}\n\\end{center}\n\n\nYou may not copy, modify, sublicense, or distribute the Document\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense, or distribute it is void, and\nwill automatically terminate your rights under this License.\n\nHowever, if you cease all violation of this License, then your license\nfrom a particular copyright holder is reinstated (a) provisionally,\nunless and until the copyright holder explicitly and finally\nterminates your license, and (b) permanently, if the copyright holder\nfails to notify you of the violation by some reasonable means prior to\n60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\nTermination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, receipt of a copy of some or all of the same material does\nnot give you any rights to use it.\n\n\n\\begin{center}\n{\\Large\\textbf{10. FUTURE REVISIONS OF THIS LICENSE} \\par}\n\\end{center}\n\n\nThe Free Software Foundation may publish new, revised versions\nof the GNU Free Documentation License from time to time.  Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.  See\n\\texttt{https://www.gnu.org/licenses/}.\n\nEach version of the License is given a distinguishing version number.\nIf the Document specifies that a particular numbered version of this\nLicense ``or any later version'' applies to it, you have the option of\nfollowing the terms and conditions either of that specified version or\nof any later version that has been published (not as a draft) by the\nFree Software Foundation.  If the Document does not specify a version\nnumber of this License, you may choose any version ever published (not\nas a draft) by the Free Software Foundation.  If the Document\nspecifies that a proxy can decide which future versions of this\nLicense can be used, that proxy's public statement of acceptance of a\nversion permanently authorizes you to choose that version for the\nDocument.\n\n\n\\begin{center}\n{\\Large\\textbf{11. RELICENSING} \\par}\n\\end{center}\n\n\n``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any\nWorld Wide Web server that publishes copyrightable works and also\nprovides prominent facilities for anybody to edit those works.  A\npublic wiki that anybody can edit is an example of such a server.  A\n``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the\nsite means any set of copyrightable works thus published on the MMC\nsite.\n\n``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0\nlicense published by Creative Commons Corporation, a not-for-profit\ncorporation with a principal place of business in San Francisco,\nCalifornia, as well as future copyleft versions of that license\npublished by that same organization.\n\n``Incorporate'' means to publish or republish a Document, in whole or\nin part, as part of another Document.\n\nAn MMC is ``eligible for relicensing'' if it is licensed under this\nLicense, and if all works that were first published under this License\nsomewhere other than this MMC, and subsequently incorporated in whole\nor in part into the MMC, (1) had no cover texts or invariant sections,\nand (2) were thus incorporated prior to November 1, 2008.\n\nThe operator of an MMC Site may republish an MMC contained in the site\nunder CC-BY-SA on the same site at any time before August 1, 2009,\nprovided the MMC is eligible for relicensing.\n\n\n\\begin{center}\n{\\Large\\textbf{ADDENDUM: How to use this License for your documents} \\par}\n\\end{center}\n\nTo use this License in a document you have written, include a copy of\nthe License in the document and put the following copyright and\nlicense notices just after the title page:\n\n\\bigskip\n\\begin{quote}\n    Copyright \\copyright{}  YEAR  YOUR NAME\\@.\n    Permission is granted to copy, distribute and/or modify this document\n    under the terms of the GNU Free Documentation License, Version 1.3\n    or any later version published by the Free Software Foundation;\n    with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.\n    A copy of the license is included in the section entitled ``GNU\n    Free Documentation License''.\n\\end{quote}\n\\bigskip\n    \nIf you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,\nreplace the ``with \\dots\\ Texts.''\\ line with this:\n\n\\bigskip\n\\begin{quote}\n    with the Invariant Sections being LIST THEIR TITLES, with the\n    Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST\\@.\n\\end{quote}\n\\bigskip\n    \nIf you have Invariant Sections without Cover Texts, or some other\ncombination of the three, merge those two alternatives to suit the\nsituation.\n\nIf your document contains nontrivial examples of program code, we\nrecommend releasing these examples in parallel under your choice of\nfree software license, such as the GNU General Public License,\nto permit their use in free software.\n\n"
  },
  {
    "path": "jgnash-manual/src/gpl-3.0.tex",
    "content": "\\chapter{GNU General Public License}\n\\begin{center}\n    Version 3, 29 June 2007 \n\\end{center}\nCopyright \\copyright\\  2007 Free Software Foundation, Inc. \\texttt{https://fsf.org/}\n\n\\bigskip\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\\section*{Preamble}\nThe GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\nThe licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\nWhen we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\nTo protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\nFor example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\nDevelopers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\nFor the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\nSome devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\nFinally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\nThe precise terms and conditions for copying, distribution and\nmodification follow.\n\n\\section*{Terms and Conditions}\n\n\\begin{enumerate}\n\n\\addtocounter{enumi}{-1}\n\n\\item Definitions.\n\n``This License'' refers to version 3 of the GNU General Public License.\n\n``Copyright'' also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n``The Program'' refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as ``you''.  ``Licensees'' and\n``recipients'' may be individuals or organizations.\n\nTo ``modify'' a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a ``modified version'' of the\nearlier work or a work ``based on'' the earlier work.\n\nA ``covered work'' means either the unmodified Program or a work based\non the Program.\n\nTo ``propagate'' a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\nTo ``convey'' a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\nAn interactive user interface displays ``Appropriate Legal Notices''\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n\\item Source Code.\n\nThe ``source code'' for a work means the preferred form of the work\nfor making modifications to it.  ``Object code'' means any non-source\nform of a work.\n\nA ``Standard Interface'' means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\nThe ``System Libraries'' of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n``Major Component'', in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\nThe ``Corresponding Source'' for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\nThe Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\nThe Corresponding Source for a work in source code form is that\nsame work.\n\n\\item Basic Permissions.\n\nAll rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\nYou may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\nConveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n\\item Protecting Users' Legal Rights From Anti-Circumvention Law.\n\nNo covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\nWhen you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n\\item Conveying Verbatim Copies.\n\nYou may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\nYou may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n\\item Conveying Modified Source Versions.\n\nYou may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n  \\begin{enumerate}\n  \\item The work must carry prominent notices stating that you modified\n  it, and giving a relevant date.\n\n  \\item The work must carry prominent notices stating that it is\n  released under this License and any conditions added under section\n  7.  This requirement modifies the requirement in section 4 to\n  ``keep intact all notices''.\n\n  \\item You must license the entire work, as a whole, under this\n  License to anyone who comes into possession of a copy.  This\n  License will therefore apply, along with any applicable section 7\n  additional terms, to the whole of the work, and all its parts,\n  regardless of how they are packaged.  This License gives no\n  permission to license the work in any other way, but it does not\n  invalidate such permission if you have separately received it.\n\n  \\item If the work has interactive user interfaces, each must display\n  Appropriate Legal Notices; however, if the Program has interactive\n  interfaces that do not display Appropriate Legal Notices, your\n  work need not make them do so.\n\\end{enumerate}\nA compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n``aggregate'' if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n\\item Conveying Non-Source Forms.\n\nYou may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n  \\begin{enumerate}\n  \\item Convey the object code in, or embodied in, a physical product\n  (including a physical distribution medium), accompanied by the\n  Corresponding Source fixed on a durable physical medium\n  customarily used for software interchange.\n\n  \\item Convey the object code in, or embodied in, a physical product\n  (including a physical distribution medium), accompanied by a\n  written offer, valid for at least three years and valid for as\n  long as you offer spare parts or customer support for that product\n  model, to give anyone who possesses the object code either (1) a\n  copy of the Corresponding Source for all the software in the\n  product that is covered by this License, on a durable physical\n  medium customarily used for software interchange, for a price no\n  more than your reasonable cost of physically performing this\n  conveying of source, or (2) access to copy the\n  Corresponding Source from a network server at no charge.\n\n  \\item Convey individual copies of the object code with a copy of the\n  written offer to provide the Corresponding Source.  This\n  alternative is allowed only occasionally and noncommercially, and\n  only if you received the object code with such an offer, in accord\n  with subsection 6b.\n\n  \\item Convey the object code by offering access from a designated\n  place (gratis or for a charge), and offer equivalent access to the\n  Corresponding Source in the same way through the same place at no\n  further charge.  You need not require recipients to copy the\n  Corresponding Source along with the object code.  If the place to\n  copy the object code is a network server, the Corresponding Source\n  may be on a different server (operated by you or a third party)\n  that supports equivalent copying facilities, provided you maintain\n  clear directions next to the object code saying where to find the\n  Corresponding Source.  Regardless of what server hosts the\n  Corresponding Source, you remain obligated to ensure that it is\n  available for as long as needed to satisfy these requirements.\n\n  \\item Convey the object code using peer-to-peer transmission, provided\n  you inform other peers where the object code and Corresponding\n  Source of the work are being offered to the general public at no\n  charge under subsection 6d.\n  \\end{enumerate}\n\nA separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\nA ``User Product'' is either (1) a ``consumer product'', which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, ``normally used'' refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n``Installation Information'' for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\nIf you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\nThe requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\nCorresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n\\item Additional Terms.\n\n``Additional permissions'' are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\nWhen you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\nNotwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n  \\begin{enumerate}\n  \\item Disclaiming warranty or limiting liability differently from the\n  terms of sections 15 and 16 of this License; or\n\n  \\item Requiring preservation of specified reasonable legal notices or\n  author attributions in that material or in the Appropriate Legal\n  Notices displayed by works containing it; or\n\n  \\item Prohibiting misrepresentation of the origin of that material, or\n  requiring that modified versions of such material be marked in\n  reasonable ways as different from the original version; or\n\n  \\item Limiting the use for publicity purposes of names of licensors or\n  authors of the material; or\n\n  \\item Declining to grant rights under trademark law for use of some\n  trade names, trademarks, or service marks; or\n\n  \\item Requiring indemnification of licensors and authors of that\n  material by anyone who conveys the material (or modified versions of\n  it) with contractual assumptions of liability to the recipient, for\n  any liability that these contractual assumptions directly impose on\n  those licensors and authors.\n  \\end{enumerate}\n\nAll other non-permissive additional terms are considered ``further\nrestrictions'' within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\nIf you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\nAdditional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n\\item Termination.\n\nYou may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\nHowever, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\nTermination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n\\item Acceptance Not Required for Having Copies.\n\nYou are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n\\item Automatic Licensing of Downstream Recipients.\n\nEach time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\nAn ``entity transaction'' is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\nYou may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n\\item Patents.\n\nA ``contributor'' is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's ``contributor version''.\n\nA contributor's ``essential patent claims'' are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, ``control'' includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\nEach contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\nIn the following three paragraphs, a ``patent license'' is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To ``grant'' such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\nIf you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  ``Knowingly relying'' means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\nIf, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\nA patent license is ``discriminatory'' if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\nNothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n\\item No Surrender of Others' Freedom.\n\nIf conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n\\item Use with the GNU Affero General Public License.\n\nNotwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n\\item Revised Versions of this License.\n\nThe Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License ``or any later version'' applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\nIf the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\nLater license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n\\item Disclaimer of Warranty.\n\n\\begin{sloppypar}\n THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\n APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE\n COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM ``AS IS''\n WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE\n RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU\\@.\n SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL\n NECESSARY SERVICING, REPAIR OR CORRECTION\\@.\n\\end{sloppypar}\n\n\\item Limitation of Liability.\n\n IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\n WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES\n AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR\n DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL\n DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM\n (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED\n INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE\n OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH\n HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGES\\@.\n\n\\item Interpretation of Sections 15 and 16.\n\nIf the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n\\begin{center}\n{\\Large\\textsc{End of Terms and Conditions} }\n\\end{center}\n\n\\begin{center}\n    {\\Large\\textbf{How to Apply These Terms to Your New Programs} }\n\\end{center}\n\nIf you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe ``copyright'' line and a pointer to where the full notice is found.\n\n{\\footnotesize\n\\begin{verbatim}\n<one line to give the program's name and a brief idea of what it does.>\n\nCopyright (C) <textyear>  <name of author>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n\\end{verbatim}\n}\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n{\\footnotesize\n\\begin{verbatim}\n<program>  Copyright (C) <year>  <name of author>\n\nThis program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\nThis is free software, and you are welcome to redistribute it\nunder certain conditions; type `show c' for details.\n\\end{verbatim}\n}\n\nThe hypothetical commands {\\texttt{show w} } and {\\texttt{show c} } should show\nthe appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an ``about box''.\n\nYou should also get your employer (if you work as a programmer) or\nschool, if any, to sign a ``copyright disclaimer'' for the program, if\nnecessary.  For more information on this, and how to apply and follow\nthe GNU GPL, see \\texttt{https://www.gnu.org/licenses/}.\n\nThe GNU General Public License does not permit incorporating your\nprogram into proprietary programs.  If your program is a subroutine\nlibrary, you may consider it more useful to permit linking proprietary\napplications with the library.  If this is what you want to do, use\nthe GNU Lesser General Public License instead of this License.  But\nfirst, please read \\texttt{https://www.gnu.org/licenses/why-not-lgpl.html}.\n\n\\end{enumerate}\n\n%\\end{document}\n"
  },
  {
    "path": "jgnash-plugin/build.gradle.kts",
    "content": "description = \"jGnash Plugin\"\n\nval moduleName = \"jgnash.plugin\"\nval javaFXVersion: String by project    // extract JavaFX version from gradle.properties\n\nplugins {\n    id(\"org.openjfx.javafxplugin\")\n}\n\ndependencies {\n    implementation(project(\":jgnash-resources\"))\n}\n\njavafx {\n    version = javaFXVersion\n    modules(\"javafx.controls\", \"javafx.fxml\")\n}\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}\n"
  },
  {
    "path": "jgnash-plugin/src/main/java/jgnash/plugin/FxPlugin.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.plugin;\n\nimport javafx.scene.Node;\n\n/**\n * This is the interface for jGnashFx application plugins.\n *\n * @author Craig Cavanaugh\n */\npublic interface FxPlugin extends Plugin {\n\n    /**\n     * This will add an additional option panel to the standard options dialog. The plugin name will be used for the\n     * tab.\n     *\n     * @return a {@code Node}. May be {@code null} if no panel is to be added\n     */\n    default Node getOptionsNode() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "jgnash-plugin/src/main/java/jgnash/plugin/Plugin.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.plugin;\n\n/**\n * This is the interface for jGnash application plugins.\n * <p>\n * Plugins can install multiple menu items into the primary UI. Any returned {@code JMenuItem}s must have the\n * client property {@code PRECEDINGMENUIDREF} set for the menu item to installed correctly.\n * <p>\n * The available preceding id for installation may be found in {@code main-frame-actions.xml} in the\n * {@code jgnash.resource} package.\n * <p>\n * Example:\n * <p>\n * {@code\n * JMenuItem item = new JMenuItem(\"Test Plugin\");\n * <p>\n * item.putClientProperty(Plugin.PRECEDINGMENUIDREF, \"paste-command\");\n * }\n * <p>\n * The above example will install a menu item after <tt>Paste</tt> in the <tt>Edit</tt> menu.\n *\n * @author Leif-Erik Dörr\n * @author Craig Cavanaugh\n */\npublic interface Plugin {\n\n    /**\n     * Client property key for the name of the options tab to add.\n     */\n    String OPTIONS_NAME = \"OptionsName\";\n\n    /**\n     * Return a descriptive name for the plugin.\n     *\n     * @return name of the plugin\n     */\n    String getName();\n\n    /**\n     * Called by the PluginFactory to start the plugin.\n     */\n    @SuppressWarnings(\"unused\")\n\tdefault void start(PluginPlatform pluginPlatform) {\n    }\n\n    /**\n     * Called by the PluginFactory to stop the plugin.\n     */\n    default void stop() {\n    }\n\n    /**\n     * Plugin platform identifier.\n     */\n    enum PluginPlatform {\n        Fx\n    }\n}\n"
  },
  {
    "path": "jgnash-plugin/src/main/java/jgnash/plugin/PluginFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.plugin;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.lang.reflect.InvocationTargetException;\nimport java.math.BigDecimal;\nimport java.net.JarURLConnection;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.net.URLDecoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Predicate;\nimport java.util.jar.Attributes;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.resource.util.OS;\n\n/**\n * Plugin Factory methods.\n *\n * @author Leif-Erik Dörr\n * @author Craig Cavanaugh\n */\npublic final class PluginFactory {\n\n    private final static String PLUGIN_DIRECTORY_NAME = \"plugins\";\n\n    private final static BigDecimal INTERFACE_VERSION = new BigDecimal(\"2.25\");\n\n    private static final String PLUGIN_PATH_MESSAGE = \"Plugin path: {0}\";\n\n    private static final Logger logger = Logger.getLogger(PluginFactory.class.getName());\n\n    private static final List<Plugin> plugins = new ArrayList<>();\n\n    private static final AtomicBoolean pluginsStarted = new AtomicBoolean(false);\n\n    private static final AtomicBoolean pluginsLoaded = new AtomicBoolean(false);\n\n    private static final String separator = System.getProperty(\"file.separator\");\n\n    private PluginFactory() {\n        // Utility class\n    }\n\n    public static List<Plugin> getPlugins() {\n        return Collections.unmodifiableList(plugins);\n    }\n\n    private static synchronized String getDefaultPluginDirectory() {\n\n        String pluginDirectory = PluginFactory.class.getProtectionDomain().getCodeSource().getLocation().getPath();\n\n        // decode to correctly handle spaces, etc. in the returned path\n        try {\n            pluginDirectory = URLDecoder.decode(pluginDirectory, StandardCharsets.UTF_8.name());\n        } catch (UnsupportedEncodingException ex) {\n            logger.log(Level.SEVERE, null, ex);\n        }\n\n        // starting path will be the lib directory because that is where jgnash-core lives.\n\n        pluginDirectory = new File(pluginDirectory).getParentFile().getParent();\n        pluginDirectory += separator + PLUGIN_DIRECTORY_NAME + separator;\n\n        logger.log(Level.INFO, PLUGIN_PATH_MESSAGE, pluginDirectory);\n\n        return pluginDirectory;\n    }\n\n    private static synchronized String getUserPluginDirectory() {\n\n        String pluginDirectory = System.getProperty(\"user.home\");\n\n        // decode to correctly handle spaces, etc. in the returned path\n        try {\n            pluginDirectory = URLDecoder.decode(pluginDirectory, StandardCharsets.UTF_8.name());\n        } catch (UnsupportedEncodingException ex) {\n            logger.log(Level.SEVERE, null, ex);\n        }\n\n        if (OS.isSystemWindows()) {\n            pluginDirectory += separator + \"AppData\" + separator + \"Local\" + separator\n                    + \"jgnash\" + separator + PLUGIN_DIRECTORY_NAME + separator;\n        } else { // unix, osx\n            pluginDirectory += separator + \".jgnash\" + separator + PLUGIN_DIRECTORY_NAME + separator;\n        }\n\n        logger.log(Level.INFO, PLUGIN_PATH_MESSAGE, pluginDirectory);\n\n        return pluginDirectory;\n    }\n\n    public static void startPlugins(final Plugin.PluginPlatform pluginPlatform) {\n        if (!pluginsStarted.getAndSet(true)) {\n            for (final Plugin plugin : plugins) {\n                logger.log(Level.INFO, \"Starting plugin: {0}\", plugin.getName());\n                plugin.start(pluginPlatform);\n            }\n        }\n    }\n\n    public static void stopPlugins() {\n        if (pluginsStarted.getAndSet(false)) {\n            for (final Plugin plugin : plugins) {\n                logger.log(Level.INFO, \"Stopping plugin: {0}\", plugin.getName());\n                plugin.stop();\n            }\n        }\n    }\n\n    /**\n     * Loads Plugins.\n     *\n     * @param predicate Predicate allows filtering and control of loading plugins\n     */\n    public static void loadPlugins(final Predicate<Plugin> predicate) {\n        if (!pluginsLoaded.getAndSet(true)) {\n            final List<String> paths = getPluginPaths();\n\n            if (!paths.isEmpty()) {\n                for (final String plugin : paths) {\n                    try {\n                        final Plugin p = loadPlugin(plugin);\n                        if (p != null) {\n                            if (predicate.test(p)) {\n                                plugins.add(p);\n                            }\n                        }\n                    } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException\n                            | IOException ex) {\n                        logger.log(Level.SEVERE, null, ex);\n                    }\n                }\n            } else {\n                logger.info(\"Did not find any plugins\");\n            }\n        }\n    }\n\n    private static List<String> getPluginPaths() {\n        final List<String> paths = new ArrayList<>();\n\n        getPluginPaths(getDefaultPluginDirectory(), paths);\n        getPluginPaths(getUserPluginDirectory(), paths);\n\n        return paths;\n    }\n\n    private static void getPluginPaths(final String root, final List<String> paths) {\n        final String[] files = new File(root).list(new PluginFilenameFilter());\n\n        if (files != null) {\n            for (final String file : files) {\n                paths.add(root + file);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"resource\")\n\tprivate static Plugin loadPlugin(final String jarFileName) throws ClassNotFoundException, InstantiationException,\n            IllegalAccessException, IOException {\n       \n        final JarURLClassLoader classLoader = new JarURLClassLoader(new URL(\"file:///\" + jarFileName));\n        \n        // Add a shutdown hook to properly close the classLoader.  It needs to remain open for the duration of \n        // the application otherwise the plugin will not be able to load any needed classes.\n        Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n            try {\n                classLoader.close();\n            } catch (final IOException e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n        }));\n\n        final String pluginActivator = classLoader.getActivator();\n\n        Plugin plugin = null;\n\n        try {\n\n            if (pluginActivator != null) {\n                final Object object = classLoader.loadClass(pluginActivator).getDeclaredConstructor().newInstance();\n\n                if (object instanceof Plugin) {\n                    plugin = (Plugin) object;\n                }\n            } else {\n                logger.log(Level.SEVERE, \"''{0}'' Plugin Interface was not implemented\", jarFileName);\n            }\n        } catch (final NoClassDefFoundError | NoSuchMethodException | InvocationTargetException e) {\n            // This is expected when a Swing instance tries to load a Fx instance of a plugin\n            logger.log(Level.INFO, \"Plugin type was not compatible; not loaded: \" + jarFileName);\n        }\n\n        return plugin;\n    }\n\n    private static class JarURLClassLoader extends URLClassLoader {\n\n        private static final String PLUGIN_ACTIVATOR = \"Plugin-Activator\";\n\n        private static final String PLUGIN_VERSION = \"Plugin-Version\";\n\n        JarURLClassLoader(final URL url) {\n            super(new URL[]{url});\n        }\n\n        String getActivator() throws IOException {\n\n            String activator = null;\n\n            final URL u = new URL(\"jar\", \"\", getURLs()[0] + \"!/\");\n            final JarURLConnection uc = (JarURLConnection) u.openConnection();\n            final Attributes attr = uc.getMainAttributes();\n\n            if (attr != null) {\n\n                BigDecimal version = null;\n                try {\n                    String value = attr.getValue(PLUGIN_VERSION);\n\n                    if (value != null) {\n                        version = new BigDecimal(attr.getValue(PLUGIN_VERSION));\n                    }\n                } catch (Exception ex) {\n                    logger.log(Level.SEVERE, null, ex);\n                }\n\n                if (version != null && INTERFACE_VERSION.compareTo(version) == 0) {\n                    activator = attr.getValue(PLUGIN_ACTIVATOR);\n                } else {\n                    logger.log(Level.WARNING, \"Plugin version not compatible; not loaded: \"\n                            + attr.getValue(PLUGIN_ACTIVATOR));\n                }\n            }\n\n            return activator;\n        }\n    }\n\n    private static class PluginFilenameFilter implements FilenameFilter {\n\n        @Override\n        public boolean accept(final File dir, final String name) {\n            return name.endsWith(\".jar\");\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/build.gradle.kts",
    "content": "description = \"jGnash Report Core\"\n\nval moduleName = \"jgnash.report\"\n\nval apachePoiVersion: String by project\nval pdfBoxVersion: String by project\nval commonsLangVersion: String by project\n\nplugins {\n    `java-library`\n}\n\ndependencies {\n    implementation(project(\":jgnash-resources\"))\n    implementation(project(\":jgnash-core\"))\n\n    implementation(\"org.apache.poi:poi-ooxml:$apachePoiVersion\") {\n        exclude(module = \"stax-api\")\n        exclude(module = \"xml-apis\")\n    }\n\n    implementation(\"org.apache.pdfbox:pdfbox:$pdfBoxVersion\")\n    implementation(\"org.apache.pdfbox:pdfbox-tools:$pdfBoxVersion\")\n    implementation(\"org.apache.commons:commons-lang3:$commonsLangVersion\")\n}\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/pdf/Constants.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.pdf;\n\n/**\n * Print / Report specific constants\n *\n * @author Craig Cavanaugh\n */\npublic class Constants {\n    public static final float POINTS_PER_INCH = 72;\n    public static final float POINTS_PER_MM = (1 / 25.4f) * POINTS_PER_INCH;\n\n    private Constants() {\n        // utility class\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/pdf/FontRegistry.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.pdf;\n\nimport jgnash.util.LogUtil;\nimport org.apache.fontbox.util.autodetect.FontFileFinder;\n\nimport java.awt.Font;\nimport java.awt.FontFormatException;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Logger;\n\n/**\n * Utility class map font names to font files\n *\n * @author Craig Cavanaugh\n */\npublic class FontRegistry {\n\n    /**\n     * Maps the report file to the report name for embedding fonts in PDF files\n     */\n    private static final Map<String, String> registeredFontMap = new ConcurrentHashMap<>();\n\n    private static final AtomicBoolean registrationComplete = new AtomicBoolean(false);\n\n    private static final AtomicBoolean registrationStarted = new AtomicBoolean(false);\n\n    private static final Lock lock = new ReentrantLock();\n\n    private static final Condition isComplete = lock.newCondition();\n\n    private FontRegistry() {\n    }\n\n    public static List<String> getFontList() {\n        blockForFontRegistration();\n\n        final ArrayList<String> fontList = new ArrayList<>(registeredFontMap.keySet());\n\n        Collections.sort(fontList);\n\n        return fontList;\n    }\n\n    private static void blockForFontRegistration() {\n        if (!registrationStarted.get()) {\n            registerFonts();\n        }\n\n        if (!registrationComplete.get()) {\n\n            try {\n                lock.lock();\n\n                while (!registrationComplete.get()) {\n                    isComplete.await();\n                }\n            } catch (final InterruptedException ex) {\n                LogUtil.logSevere(FontRegistry.class, ex);\n            } finally {\n                lock.unlock();\n            }\n        }\n    }\n\n    static String getRegisteredFontPath(final String name) {\n        blockForFontRegistration();\n\n        return registeredFontMap.get(name);\n    }\n\n    private static void registerFonts() {\n\n        if (!registrationStarted.get()) {\n            registrationStarted.set(true);\n\n            final Thread thread = new Thread(() -> {\n                lock.lock();\n\n                try {\n                    FontRegistry.registerFontDirectories();\n\n                    registrationComplete.set(true);\n                    isComplete.signal();\n\n                    Logger.getLogger(FontRegistry.class.getName()).info(\"Font registration is complete\");\n                } finally {\n                    lock.unlock();\n                }\n            });\n\n            thread.setPriority(Thread.MIN_PRIORITY);\n            thread.start();\n        }\n    }\n\n    private static void registerFont(final String path) {\n        try {\n            if (path.toLowerCase(Locale.ROOT).endsWith(\".ttf\") || path.toLowerCase(Locale.ROOT).endsWith(\".otf\")\n                    || path.toLowerCase(Locale.ROOT).indexOf(\".ttc,\") > 0) {\n\n                try (final FileInputStream fileInputStream = new FileInputStream(path)) {\n                    final Font font = Font.createFont(Font.TRUETYPE_FONT, fileInputStream);\n                    registeredFontMap.put(font.getName(), path);\n                }\n            } else if (path.toLowerCase(Locale.ROOT).endsWith(\".afm\") || path.toLowerCase(Locale.ROOT).endsWith(\".pfm\")) {\n                try (final FileInputStream fileInputStream = new FileInputStream(path)) {\n                    final Font font = Font.createFont(Font.TYPE1_FONT, fileInputStream);\n                    registeredFontMap.put(font.getName(), path);\n                }\n            }\n        } catch (final IOException e) {\n            LogUtil.logSevere(FontRegistry.class, e);\n        } catch (final FontFormatException ffe) {\n            Logger.getLogger(FontRegistry.class.getName()).info(\"Could not register font: \" + path);\n            LogUtil.logSevere(FontRegistry.class, ffe);\n        }\n    }\n\n    /**\n     * Register fonts in known directories.\n     */\n    private static void registerFontDirectories() {\n        new FontFileFinder().find().stream().map(uri\n                -> new File(uri).getAbsolutePath()).forEach(FontRegistry::registerFont);\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/pdf/PageSize.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.pdf;\n\nimport static jgnash.report.pdf.Constants.POINTS_PER_INCH;\nimport static jgnash.report.pdf.Constants.POINTS_PER_MM;\n\n/**\n * Enumeration of standard page sizes.\n * <p>\n * Page sizes are in Points\n *\n * @author Craig Cavanaugh\n */\npublic enum PageSize {\n\n    A2(\"ISO A2\", 420 * POINTS_PER_MM, 594 * POINTS_PER_MM),\n    A3(\"ISO A3\", 297 * POINTS_PER_MM, 420 * POINTS_PER_MM),\n    A4(\"ISO A4\", 210 * POINTS_PER_MM, 297 * POINTS_PER_MM),\n    LETTER(\"US Letter\", 8.5f * POINTS_PER_INCH, 11f * POINTS_PER_INCH),\n    LEGAL(\"US Legal\", 8.5f * POINTS_PER_INCH, 14f * POINTS_PER_INCH),\n    TABLOID(\"US Tabloid\", 11f * POINTS_PER_INCH, 17f * POINTS_PER_INCH);\n\n    public final transient float width;\n    public final transient float height;\n\n    private final transient String description;\n\n    PageSize(final String description, final float width, final float height) {\n        this.description = description;\n        this.width = width;\n        this.height = height;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/pdf/Report.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.pdf;\n\nimport java.awt.Color;\nimport java.awt.image.BufferedImage;\nimport java.awt.print.PageFormat;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.text.MessageFormat;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\nimport java.util.Locale;\nimport java.util.Objects;\nimport java.util.ResourceBundle;\nimport java.util.Set;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\n\nimport jgnash.engine.MathConstants;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.report.table.GroupInfo;\nimport jgnash.report.ui.ReportPrintFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\nimport org.apache.commons.lang3.tuple.ImmutablePair;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.pdfbox.io.MemoryUsageSetting;\nimport org.apache.pdfbox.pdmodel.PDDocument;\nimport org.apache.pdfbox.pdmodel.PDPage;\nimport org.apache.pdfbox.pdmodel.PDPageContentStream;\nimport org.apache.pdfbox.pdmodel.common.PDRectangle;\nimport org.apache.pdfbox.pdmodel.font.PDFont;\nimport org.apache.pdfbox.pdmodel.font.PDType0Font;\nimport org.apache.pdfbox.pdmodel.font.PDType1Font;\nimport org.apache.pdfbox.rendering.ImageType;\nimport org.apache.pdfbox.rendering.PDFRenderer;\n\nimport static jgnash.util.LogUtil.logSevere;\n\n/**\n * Base report format definition.\n * <p>\n * Units of measure is in Points\n * <p>\n * The origin of a PDFBox page is the bottom left corner vs. a report being created from the top down.  Report layout\n * logic is from top down with use of a method to convert to PDF coordinate system.\n * <p>\n * This class is abstract to force isolation of Preferences through simple extension of the class\n * <p>\n *\n * @author Craig Cavanaugh\n */\n@SuppressWarnings(\"WeakerAccess\")\npublic abstract class Report implements AutoCloseable {\n\n    private static final int MAX_MEMORY_USAGE = 10_000_000;    // allow 10 meg reports in memory before a scratch file is used\n\n    private static final String BASE_FONT_SIZE = \"baseFontSize\";\n\n    protected static final ResourceBundle rb = ResourceUtils.getBundle();\n\n    private static final int DEFAULT_BASE_FONT_SIZE = 11;\n\n    private String ellipsis = \"...\";\n\n    private float baseFontSize;\n\n    private PDFont tableFont;\n\n    private PDFont headerFont;\n\n    private PDFont footerFont;\n\n    private float cellPadding = 3f; // cell padding in points\n\n    final Color footerBackGround = Color.LIGHT_GRAY;\n\n    final Color headerBackground = Color.DARK_GRAY;\n\n    final Color headerTextColor = Color.WHITE;\n\n    static final float FOOTER_SCALE = 0.80f;\n\n    static final float DEFAULT_LINE_WIDTH = 0.20f;\n\n    final PDDocument pdfDocument;\n\n    private PageFormat pageFormat;\n\n    private boolean forceGroupPagination = false;\n\n    public Report() {\n        this.pdfDocument = new PDDocument(MemoryUsageSetting.setupMixed(MAX_MEMORY_USAGE));\n\n        setTableFont(loadFont(ReportFactory.getMonoFont(), pdfDocument));\n        setHeaderFont(loadFont(ReportFactory.getHeaderFont(), pdfDocument));\n        setFooterFont(loadFont(ReportFactory.getProportionalFont(), pdfDocument));\n\n        // restore font size\n        baseFontSize = getPreferences().getFloat(BASE_FONT_SIZE, DEFAULT_BASE_FONT_SIZE);\n\n        // restore the page format\n        setPageFormat(getPageFormat());\n    }\n\n    public void clearReport() {\n        for (PDPage pdPage : pdfDocument.getPages()) {\n            pdfDocument.removePage(pdPage);\n        }\n    }\n\n    private static PDFont loadFont(final String name, final PDDocument document) {\n\n        final String path = FontRegistry.getRegisteredFontPath(name);\n\n        if (path != null && !path.isEmpty()) {\n            try {\n                if (path.toLowerCase(Locale.ROOT).endsWith(\".ttf\") || path.toLowerCase(Locale.ROOT).endsWith(\".otf\")\n                        || path.toLowerCase(Locale.ROOT).indexOf(\".ttc,\") > 0) {\n                    return PDType0Font.load(document, new FileInputStream(path), false);\n                } else if (path.toLowerCase(Locale.ROOT).endsWith(\".afm\") || path.toLowerCase(Locale.ROOT).endsWith(\".pfm\")) {\n                    return new PDType1Font(document, new FileInputStream(path));\n                }\n            } catch (final Exception ignored) {\n            }\n        }\n\n        return PDType1Font.COURIER;\n    }\n\n    public int getPageCount() {\n        return pdfDocument.getNumberOfPages();\n    }\n\n    public final PageFormat getPageFormat() {\n        if (pageFormat == null) {\n            pageFormat = ReportPrintFactory.getPageFormat(getPreferences());\n        }\n\n        return pageFormat;\n    }\n\n    public final void setPageFormat(@NotNull final PageFormat pageFormat) {\n        Objects.requireNonNull(pageFormat);\n\n        this.pageFormat = pageFormat;\n        ReportPrintFactory.savePageFormat(getPreferences(), pageFormat);\n    }\n\n    @NotNull\n    public PDFont getTableFont() {\n        return tableFont;\n    }\n\n    public void setTableFont(@NotNull PDFont tableFont) {\n        this.tableFont = tableFont;\n    }\n\n    public float getBaseFontSize() {\n        return baseFontSize;\n    }\n\n    public void setBaseFontSize(float tableFontSize) {\n        this.baseFontSize = tableFontSize;\n        getPreferences().putFloat(BASE_FONT_SIZE, tableFontSize);\n    }\n\n    private float getTableRowHeight() {\n        return getBaseFontSize() + 2 * getCellPadding();\n    }\n\n    public boolean isLandscape() {\n        return getPageFormat().getWidth() > getPageFormat().getHeight();\n    }\n\n    public PDFont getHeaderFont() {\n        return headerFont;\n    }\n\n    public void setHeaderFont(final PDFont headerFont) {\n        this.headerFont = headerFont;\n    }\n\n    public float getCellPadding() {\n        return cellPadding;\n    }\n\n    public void setCellPadding(final float cellPadding) {\n        this.cellPadding = cellPadding;\n    }\n\n    private float getAvailableWidth() {\n        return (float) getPageFormat().getImageableWidth();\n    }\n\n    private float getLeftMargin() {\n        return (float) getPageFormat().getImageableX();\n    }\n\n    private float getTopMargin() {\n        return (float) getPageFormat().getImageableY();\n    }\n\n    private float getRightMargin() {\n        return (float) getPageFormat().getWidth() - getAvailableWidth() - getLeftMargin();\n    }\n\n    private float getBottomMargin() {\n        return (float) (getPageFormat().getHeight() - getPageFormat().getImageableHeight() - getTopMargin());\n    }\n\n    private float getFooterFontSize() {\n        return (float) Math.ceil(getBaseFontSize() * FOOTER_SCALE);\n    }\n\n    public PDFont getFooterFont() {\n        return footerFont;\n    }\n\n    public void setFooterFont(final PDFont footerFont) {\n        this.footerFont = footerFont;\n    }\n\n    /**\n     * Returns the legend for the grand total\n     *\n     * @return report name\n     */\n    public String getGrandTotalLegend() {\n        return ResourceUtils.getString(\"Word.Total\");\n    }\n\n    /**\n     * Returns the general label for the group footer\n     *\n     * @return footer label\n     */\n    public String getGroupFooterLabel() {\n        return ResourceUtils.getString(\"Word.Subtotal\");\n    }\n\n    public void addTable(final AbstractReportTableModel reportModel) throws IOException {\n\n        final String title = reportModel.getTitle();\n        final String subTitle = reportModel.getSubTitle();\n\n        boolean titleWritten = false;\n\n        final float[] columnWidths = getColumnWidths(reportModel);\n\n        final Set<GroupInfo> groupInfoSet = GroupInfo.getGroups(reportModel);\n\n        // calculate the maximum imageable height of the page before we get too close to the footer\n        float imageableBottom = (float) getPageFormat().getHeight() - getBottomMargin() - getTableRowHeight();\n\n        float docY = getTopMargin();   // start at top of the page with the margin\n\n        PDPage page = createPage(); // create the first page\n\n        for (final GroupInfo groupInfo : groupInfoSet) {\n\n            int row = 0;  // tracks the last written row\n\n            while (row < reportModel.getRowCount()) {\n\n                if (docY > imageableBottom || isForceGroupPagination()) {    // if near the bottom of the page\n                    docY = getTopMargin();   // start at top of the page with the margin\n                    page = createPage();\n                }\n\n                try (final PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page,\n                        PDPageContentStream.AppendMode.APPEND, false)) {\n\n                    // add the table title if its not been added\n                    if (title != null && !title.isEmpty() && row == 0 && !titleWritten) {\n                        docY = addReportTitle(contentStream, title, subTitle, docY);\n\n                        titleWritten = true;\n                    }\n\n                    // add the group subtitle if needed\n                    if (groupInfoSet.size() > 1) {\n                        docY = addTableTitle(contentStream, groupInfo.group, docY);\n                    }\n\n                    // write a section of the table and save the last row written for next page if needed\n                    final Pair<Integer, Float> pair\n                            = addTableSection(reportModel, groupInfo.group, contentStream, row, columnWidths, docY);\n\n                    row = pair.getLeft();\n                    docY = pair.getRight();\n\n                } catch (final IOException e) {\n                    logSevere(Report.class, e);\n                    throw (e);\n                }\n\n                // check to see if this table has summation information and add a summation footer\n                if (groupInfo.hasSummation() && row == reportModel.getRowCount()) {\n\n                    // TODO, make sure the end of the page has not been reached\n                    try (final PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page,\n                            PDPageContentStream.AppendMode.APPEND, false)) {\n                        docY = addTableFooter(reportModel, groupInfo, contentStream, columnWidths, docY);\n                        docY += getBaseFontSize();  // add some padding\n                    } catch (final IOException e) {\n                        logSevere(Report.class, e);\n                        throw (e);\n                    }\n                }\n            }\n        }\n\n        if (reportModel.hasGlobalSummary()) {\n            try (final PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page,\n                    PDPageContentStream.AppendMode.APPEND, false)) {\n                addGlobalFooter(reportModel, contentStream, columnWidths, docY);\n            } catch (final IOException e) {\n                logSevere(Report.class, e);\n                throw (e);\n            }\n        }\n    }\n\n    /**\n     * Simply transform function to convert from a upper origin to a lower pdf page origin.\n     *\n     * @param y document y position\n     * @return returns the pdf page y position\n     */\n    private float docYToPageY(final float y) {\n        return (float) getPageFormat().getHeight() - y;\n    }\n\n    /**\n     * Writes a table section to the report.\n     *\n     * @param reportModel   report model\n     * @param group         report group\n     * @param contentStream PDF content stream\n     * @param startRow      starting row\n     * @param columnWidths  column widths\n     * @param yStart        start location from top of the page\n     * @return returns the last reported row of the group and yDoc location\n     * @throws IOException IO exception\n     */\n    @SuppressWarnings(\"SuspiciousNameCombination\")\n    private Pair<Integer, Float> addTableSection(final AbstractReportTableModel reportModel, @NotNull final String group,\n                                                 final PDPageContentStream contentStream, final int startRow, float[] columnWidths,\n                                                 float yStart) throws IOException {\n\n        Objects.requireNonNull(group);\n\n        int rowsWritten = 0;    // the return value of the number of rows written\n\n        // establish start location, use half the row height as the vertical margin between title and table\n        final float yTop = (float) getPageFormat().getHeight() - getTableRowHeight() / 2 - yStart;\n\n        float xPos = getLeftMargin() + getCellPadding();\n        float yPos = yTop - getTableRowHeight() + getRowTextBaselineOffset();\n\n        contentStream.setFont(getHeaderFont(), getBaseFontSize());\n\n        // add the header\n        contentStream.setNonStrokingColor(headerBackground);\n        fillRect(contentStream, getLeftMargin(), yTop - getTableRowHeight(), getAvailableWidth(), getTableRowHeight());\n\n        contentStream.setNonStrokingColor(headerTextColor);\n\n        for (int i = 0; i < reportModel.getColumnCount(); i++) {\n            if (reportModel.isColumnVisible(i)) {\n                float shift = 0;\n                float availWidth = columnWidths[i] - getCellPadding() * 2;\n\n                final String text = truncateText(reportModel.getColumnName(i), availWidth,\n                        getHeaderFont(), getBaseFontSize());\n\n                if (rightAlign(i, reportModel)) {\n                    shift = availWidth - getStringWidth(text, getHeaderFont(), getBaseFontSize());\n                }\n\n                drawText(contentStream, xPos + shift, yPos, text);\n\n                xPos += columnWidths[i];\n            }\n        }\n\n        // add the rows\n        contentStream.setFont(getTableFont(), getBaseFontSize());\n        contentStream.setNonStrokingColor(Color.BLACK);\n\n        int row = startRow;\n\n        final float bottomMargin = getBottomMargin();\n\n        while (yPos > bottomMargin + getTableRowHeight() && row < reportModel.getRowCount()) {\n\n            final String rowGroup = reportModel.getGroup(row);\n\n            if (group.equals(rowGroup)) {\n\n                xPos = getLeftMargin() + getCellPadding();\n                yPos -= getTableRowHeight();\n\n                for (int i = 0; i < reportModel.getColumnCount(); i++) {\n\n                    if (reportModel.isColumnVisible(i)) {\n\n                        final Object value = reportModel.getValueAt(row, i);\n\n                        if (value != null) {\n                            float shift = 0;\n                            float availWidth = columnWidths[i] - getCellPadding() * 2;\n\n                            final String text = truncateText(formatValue(reportModel.getValueAt(row, i), i, reportModel), availWidth,\n                                    getTableFont(), getBaseFontSize());\n\n                            if (rightAlign(i, reportModel)) {\n                                shift = availWidth - getStringWidth(text, getTableFont(), getBaseFontSize());\n                            }\n\n                            drawText(contentStream, xPos + shift, yPos, text);\n                        }\n\n                        xPos += columnWidths[i];\n                    }\n                }\n\n                rowsWritten++;\n            }\n            row++;\n        }\n\n        // add row lines\n        yPos = yTop;\n        xPos = getLeftMargin();\n\n        for (int r = 0; r <= rowsWritten + 1; r++) {\n            drawLine(contentStream, xPos, yPos, getAvailableWidth() + getLeftMargin(), yPos);\n            yPos -= getTableRowHeight();\n        }\n\n        // add column lines\n        yPos = yTop;\n        xPos = getLeftMargin();\n\n        for (int i = 0; i < reportModel.getColumnCount(); i++) {\n            if (reportModel.isColumnVisible(i)) {\n                drawLine(contentStream, xPos, yPos, xPos, yPos - getTableRowHeight() * (rowsWritten + 1));\n                xPos += columnWidths[i];\n            }\n        }\n\n        // end of last column\n        drawLine(contentStream, xPos, yPos, xPos, yPos - getTableRowHeight() * (rowsWritten + 1));\n\n        float yDoc = (float) getPageFormat().getHeight() - (yPos - getTableRowHeight() * (rowsWritten + 1));\n\n        // return the row and docY position\n        return new ImmutablePair<>(row, yDoc);\n    }\n\n    /**\n     * Calculates the offset for row text\n     *\n     * @return offset\n     */\n    private float getRowTextBaselineOffset() {\n        return (getTableRowHeight()\n                - getTableFont().getFontDescriptor().getCapHeight() / 1000 * getBaseFontSize()) / 2f;\n    }\n\n    /**\n     * Writes a table footer to the report.\n     *\n     * @param reportModel   report model\n     * @param groupInfo     Group info to report on\n     * @param contentStream PDF content stream\n     * @param columnWidths  column widths\n     * @param yStart        start location from top of the page\n     * @return returns the y position from the top of the page\n     * @throws IOException IO exception\n     */\n    private float addTableFooter(final AbstractReportTableModel reportModel, final GroupInfo groupInfo,\n                                 final PDPageContentStream contentStream, float[] columnWidths,\n                                 float yStart) throws IOException {\n\n        float yDoc = yStart + getTableRowHeight();\n\n        // add the footer background\n        contentStream.setNonStrokingColor(footerBackGround);\n        fillRect(contentStream, getLeftMargin(), docYToPageY(yDoc), getAvailableWidth(), getTableRowHeight());\n\n        drawLine(contentStream, getLeftMargin(), docYToPageY(yDoc), getAvailableWidth() + getLeftMargin(), docYToPageY(yDoc));\n        drawLine(contentStream, getLeftMargin(), docYToPageY(yDoc - getTableRowHeight()), getLeftMargin(), docYToPageY(yDoc));\n        drawLine(contentStream, getLeftMargin() + getAvailableWidth(), docYToPageY(yDoc - getTableRowHeight()),\n                getLeftMargin() + getAvailableWidth(), docYToPageY(yDoc));\n\n        contentStream.setFont(getTableFont(), getBaseFontSize());\n        contentStream.setNonStrokingColor(Color.BLACK);\n\n        // draw summation values\n        float xPos = getLeftMargin() + getCellPadding();\n\n        // search for first visible column width\n        for (int c = 0; c < reportModel.getColumnCount(); c++) {\n            if (reportModel.isColumnVisible(c)) {\n\n                // right align the text\n                final float availWidth = columnWidths[c] - getCellPadding() * 2;\n                final float shift = availWidth - getStringWidth(reportModel.getGroupFooterLabel(), getTableFont(), getBaseFontSize());\n\n                drawText(contentStream, xPos + shift, docYToPageY(yDoc - getRowTextBaselineOffset()),\n                        reportModel.getGroupFooterLabel());\n\n                break;\n            }\n        }\n\n\n        for (int c = 0; c < reportModel.getColumnCount(); c++) {\n\n            if (reportModel.isColumnVisible(c) && reportModel.isColumnSummed(c)) {\n\n                final Object value = groupInfo.getValue(c);\n\n                if (value != null) {\n                    float shift = 0;\n                    float availWidth = columnWidths[c] - getCellPadding() * 2;\n\n                    final String text = truncateText(formatValue(groupInfo.getValue(c), c, reportModel), availWidth,\n                            getTableFont(), getBaseFontSize());\n\n                    if (rightAlign(c, reportModel)) {\n                        shift = availWidth - getStringWidth(text, getTableFont(), getBaseFontSize());\n                    }\n\n                    drawText(contentStream, xPos + shift, docYToPageY(yDoc - getRowTextBaselineOffset()), text);\n                }\n            }\n\n            if (c < reportModel.getColumnCount() - 1) {\n                xPos += columnWidths[c];\n            }\n        }\n\n        return yDoc;\n    }\n\n    /**\n     * Writes a table footer to the report.\n     *\n     * @param reportModel   report model\n     * @param contentStream PDF content stream\n     * @param columnWidths  column widths\n     * @param yStart        start location from top of the page\n     * @throws IOException IO exception\n     */\n    private void addGlobalFooter(final AbstractReportTableModel reportModel, final PDPageContentStream contentStream,\n                                 float[] columnWidths, float yStart) throws IOException {\n\n        float yDoc = yStart + getTableRowHeight();\n\n        // add the footer background\n        contentStream.setNonStrokingColor(footerBackGround);\n        fillRect(contentStream, getLeftMargin(), docYToPageY(yDoc), getAvailableWidth(), getTableRowHeight());\n\n        drawLine(contentStream, getLeftMargin(), docYToPageY(yDoc), getAvailableWidth() + getLeftMargin(), docYToPageY(yDoc));\n        drawLine(contentStream, getLeftMargin(), docYToPageY(yDoc - getTableRowHeight()), getLeftMargin(), docYToPageY(yDoc));\n        drawLine(contentStream, getLeftMargin() + getAvailableWidth(), docYToPageY(yDoc - getTableRowHeight()),\n                getLeftMargin() + getAvailableWidth(), docYToPageY(yDoc));\n\n        contentStream.setFont(getTableFont(), getBaseFontSize());\n        contentStream.setNonStrokingColor(Color.BLACK);\n\n        // draw summation values\n        float xPos = getLeftMargin() + getCellPadding();\n\n        // search for first visible column width\n        for (int c = 0; c < reportModel.getColumnCount(); c++) {\n            if (reportModel.isColumnVisible(c)) {\n\n                // right align the text\n                final float availWidth = columnWidths[c] - getCellPadding() * 2;\n                final float shift = availWidth - getStringWidth(reportModel.getGrandTotalLegend(), getTableFont(), getBaseFontSize());\n\n                drawText(contentStream, xPos + shift, docYToPageY(yDoc - getRowTextBaselineOffset()),\n                        reportModel.getGrandTotalLegend());\n\n                break;\n            }\n        }\n\n        for (int c = 0; c < reportModel.getColumnCount(); c++) {\n\n            if (reportModel.isColumnVisible(c) && reportModel.isColumnSummed(c)) {\n\n                final Object value = reportModel.getGlobalSum(c);\n\n                if (value != null) {\n                    float shift = 0;\n                    float availWidth = columnWidths[c] - getCellPadding() * 2;\n\n                    final String text = truncateText(formatValue(reportModel.getGlobalSum(c), c, reportModel), availWidth,\n                            getTableFont(), getBaseFontSize());\n\n                    if (rightAlign(c, reportModel)) {\n                        shift = availWidth - getStringWidth(text, getTableFont(), getBaseFontSize());\n                    }\n\n                    drawText(contentStream, xPos + shift, docYToPageY(yDoc - getRowTextBaselineOffset()), text);\n                }\n            }\n\n            if (c < reportModel.getColumnCount() - 1) {\n                xPos += columnWidths[c];\n            }\n        }\n\n        //return yDoc;\n    }\n\n    private static String formatValue(final Object value, final int column, final AbstractReportTableModel reportModel) {\n        if (value == null) {\n            return \" \";\n        }\n\n        final ColumnStyle columnStyle = reportModel.getColumnStyle(column);\n\n        switch (columnStyle) {\n            case TIMESTAMP:\n                final DateTimeFormatter dateTimeFormatter = DateUtils.getShortDateTimeFormatter();\n                return dateTimeFormatter.format((LocalDateTime) value);\n            case SHORT_DATE:\n                final DateTimeFormatter dateFormatter = DateUtils.getShortDateFormatter();\n                return dateFormatter.format((LocalDate) value);\n            case SHORT_AMOUNT:\n                final NumberFormat shortNumberFormat = NumericFormats.getShortCommodityFormat(reportModel.getCurrencyNode());\n                return shortNumberFormat.format(value);\n            case BALANCE:\n            case BALANCE_WITH_SUM:\n            case BALANCE_WITH_SUM_AND_GLOBAL:\n            case AMOUNT_SUM:\n                final NumberFormat numberFormat = NumericFormats.getFullCommodityFormat(reportModel.getCurrencyNode());\n                return numberFormat.format(value);\n            case PERCENTAGE:\n                final NumberFormat percentageFormat = NumericFormats.getPercentageFormat();\n                return percentageFormat.format(value);\n            case QUANTITY:\n                final NumberFormat qtyFormat = NumericFormats.getFixedPrecisionFormat(MathConstants.SECURITY_QUANTITY_ACCURACY);\n                return qtyFormat.format(value);\n            default:\n                return value.toString();\n        }\n    }\n\n    private static boolean rightAlign(final int column, final AbstractReportTableModel reportModel) {\n        final ColumnStyle columnStyle = reportModel.getColumnStyle(column);\n\n        switch (columnStyle) {\n            case SHORT_AMOUNT:\n            case BALANCE:\n            case BALANCE_WITH_SUM:\n            case BALANCE_WITH_SUM_AND_GLOBAL:\n            case AMOUNT_SUM:\n            case PERCENTAGE:\n            case QUANTITY:\n                return true;\n            default:\n                return false;\n        }\n    }\n\n    private static void drawText(final PDPageContentStream contentStream, final float xStart, final float yStart,\n                                 final String text) throws IOException {\n        contentStream.beginText();\n        contentStream.newLineAtOffset(xStart, yStart);\n        contentStream.showText(text);\n        contentStream.endText();\n    }\n\n\n    private static void drawLine(final PDPageContentStream contentStream, final float xStart, final float yStart,\n                                 final float xEnd, final float yEnd) throws IOException {\n        contentStream.setLineWidth(DEFAULT_LINE_WIDTH);\n        contentStream.moveTo(xStart, yStart);\n        contentStream.lineTo(xEnd, yEnd);\n        contentStream.stroke();\n    }\n\n    private static void fillRect(final PDPageContentStream contentStream, final float x, final float y, final float width,\n                                 final float height) throws IOException {\n        contentStream.addRect(x, y, width, height);\n        contentStream.fill();\n    }\n\n    /**\n     * Adds a title to the table and returns the new document position\n     *\n     * @param title  title\n     * @param yStart table title position from the top of the page\n     * @return current y document position\n     * @throws IOException exception\n     */\n    private float addTableTitle(final PDPageContentStream contentStream, final String title, final float yStart)\n            throws IOException {\n\n        float docY = yStart + getBaseFontSize() * 1.5f;  // add for font height\n        float xPos = getLeftMargin();\n\n        contentStream.setFont(getHeaderFont(), getBaseFontSize() * 1.5f);\n        drawText(contentStream, xPos, docYToPageY(docY), title);\n\n        return docY;    // returns new y document position\n    }\n\n    /**\n     * Adds a Title and subtitle to the document and returns the height consumed\n     *\n     * @param title  title\n     * @param yStart start from the top of the page\n     * @return document y position\n     * @throws IOException exception\n     */\n    private float addReportTitle(final PDPageContentStream contentStream, final String title, final String subTitle,\n                                 final float yStart) throws IOException {\n\n        float width = getStringWidth(title, getHeaderFont(), getBaseFontSize() * 2);\n        float xPos = (getAvailableWidth() / 2f) - (width / 2f) + getLeftMargin();\n        float docY = yStart + getBaseFontSize();\n\n        contentStream.setFont(getHeaderFont(), getBaseFontSize() * 2);\n        drawText(contentStream, xPos, docYToPageY(docY), title);\n\n        if (subTitle != null && subTitle.length() > 0) {    // subtitle may be empty\n            width = getStringWidth(subTitle, getFooterFont(), getFooterFontSize());\n            xPos = (getAvailableWidth() / 2f) - (width / 2f) + getLeftMargin();\n            docY += getFooterFontSize() * 1.5f;\n\n            contentStream.setFont(getFooterFont(), getFooterFontSize());\n            drawText(contentStream, xPos, docYToPageY(docY), subTitle);\n\n            docY += getFooterFontSize() * 2.0f;   // add a margin below the sub title\n        }\n\n        return docY;\n    }\n\n    public void addFooter() throws IOException {\n\n        final String timeStamp = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).format(LocalDateTime.now());\n\n        final int pageCount = pdfDocument.getNumberOfPages();\n        float yStart = getBottomMargin() * 2 / 3;\n\n        for (int i = 0; i < pageCount; i++) {\n            final PDPage page = pdfDocument.getPage(i);\n            final String pageText = MessageFormat.format(rb.getString(\"Pattern.Pages\"), i + 1, pageCount);\n            final float width = getStringWidth(pageText, getFooterFont(), getFooterFontSize());\n\n            try (final PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page, PDPageContentStream.AppendMode.APPEND, true)) {\n                contentStream.setFont(getFooterFont(), getFooterFontSize());\n\n                drawText(contentStream, getLeftMargin(), yStart, timeStamp);\n                drawText(contentStream, (float) getPageFormat().getWidth() - getRightMargin() - width, yStart, pageText);\n            } catch (final IOException e) {\n                logSevere(Report.class, e);\n            }\n        }\n    }\n\n    private String truncateText(final String text, final float availWidth, final PDFont font, final float fontSize) throws IOException {\n        if (text != null) {\n            String content = text;\n\n            float width = getStringWidth(content, font, fontSize);\n\n            // munch down the end of the string until it fits\n            if (width > availWidth) {\n                while (getStringWidth(content + getEllipsis(), font, fontSize) > availWidth && !content.isEmpty()) {\n                    content = content.substring(0, content.length() - 1);\n                }\n\n                content = content + getEllipsis();\n            }\n\n            return content;\n        }\n\n        return null;\n    }\n\n    private PDPage createPage() {\n\n        final PDPage page = new PDPage(new PDRectangle(0f, 0f, (float) getPageFormat().getWidth(),\n                (float) getPageFormat().getHeight()));\n\n        pdfDocument.addPage(page);  // add the page to the document\n\n        return page;\n    }\n\n    private static float getStringWidth(final String text, final PDFont font, final float fontSize) throws IOException {\n        return (float) Math.ceil(font.getStringWidth(text) / 1000f * fontSize);\n    }\n\n    public String getEllipsis() {\n        return ellipsis;\n    }\n\n    public void setEllipsis(String ellipsis) {\n        this.ellipsis = ellipsis;\n    }\n\n    private float[] getColumnWidths(final AbstractReportTableModel reportModel) throws IOException {\n\n        int visibleColumnCount = 0;\n\n        for (int i = 0; i < reportModel.getColumnCount(); i++) {\n            if (reportModel.isColumnVisible(i)) {\n                visibleColumnCount++;\n            }\n        }\n\n        float[] widths = new float[reportModel.getColumnCount()]; // calculated optimal widths\n\n        float fixedWidth = 0;           // total fixed width\n        boolean compressAll = false;    // true if all columns need to be compressed\n        float minWidth = 0;             // the minimum width based on column names\n\n        for (int i = 0; i < reportModel.getColumnCount(); i++) {\n            if (reportModel.isColumnVisible(i)) {\n\n                final String protoValue = reportModel.getColumnPrototypeValueAt(i);\n\n                float headerWidth = getStringWidth(reportModel.getColumnName(i), getHeaderFont(), getBaseFontSize()) + getCellPadding() * 3f;\n                float cellTextWidth = getStringWidth(protoValue, getTableFont(), getBaseFontSize()) + getCellPadding() * 3f;\n\n                widths[i] = Math.max(headerWidth, cellTextWidth);\n\n                if (reportModel.isColumnFixedWidth(i)) {\n                    fixedWidth += widths[i];\n                }\n            } else {\n                widths[i] = 0;    // not visible, but a place holder is needed\n            }\n        }\n\n        // it gets ugly if there is simply not enough room\n        if (fixedWidth > getAvailableWidth() || minWidth > getAvailableWidth()) {\n            compressAll = true;\n            Logger.getLogger(Report.class.getName()).warning(\"Page width is not wide enough\");\n        }\n\n        for (int i = 0; i < reportModel.getColumnCount(); i++) {\n            if (reportModel.isColumnVisible(i)) {\n                if (compressAll) {  // make it ugly\n                    widths[i] = getAvailableWidth() / visibleColumnCount;\n                } else if (!reportModel.isColumnFixedWidth(i)) {\n                    float remainder = getAvailableWidth() - fixedWidth;\n\n                    widths[i] = (reportModel.getColumnWidthWeight(i) / 100f) * remainder;\n                }\n            }\n        }\n\n        return widths;\n    }\n\n    public final Preferences getPreferences() {\n        return Preferences.userNodeForPackage(getClass()).node(getClass().getSimpleName());\n    }\n\n    /**\n     * Renders the PDF report to a raster image\n     *\n     * @param pageIndex page index\n     * @param dpi       DPI for the image\n     * @return the image\n     */\n    public BufferedImage renderImage(final int pageIndex, final int dpi) {\n        final PDFRenderer pdfRenderer = new PDFRenderer(pdfDocument);\n\n        try {\n            return pdfRenderer.renderImageWithDPI(pageIndex, dpi, ImageType.RGB);\n        } catch (final IOException ioe) {   // occurs when report render is interrupted\n            Logger.getLogger(Report.class.getName()).warning(ioe.getLocalizedMessage());\n            return new BufferedImage(1,1, BufferedImage.TYPE_INT_RGB);\n        }\n    }\n\n    /**\n     * Saves the report to a PDF file\n     *\n     * @param path Path to save to\n     * @throws IOException exception\n     */\n    public void saveToFile(final Path path) throws IOException {\n        pdfDocument.save(path.toFile());\n    }\n\n    @Override\n    public void close() throws IOException {\n        pdfDocument.close();\n        Logger.getLogger(Report.class.getName()).info(\"Closed the PDDocument cleanly\");\n    }\n\n    public boolean isForceGroupPagination() {\n        return forceGroupPagination;\n    }\n\n    public void setForceGroupPagination(boolean forceGroupPagination) {\n        this.forceGroupPagination = forceGroupPagination;\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/pdf/ReportFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.pdf;\n\nimport java.util.List;\nimport java.util.prefs.Preferences;\n\n/**\n * Factory methods to help with report configuration and generation\n * \n * @author Craig Cavanaugh\n */\npublic class ReportFactory {\n\n    /**\n     * Preferences key for mono spaced report\n     */\n    private final static String MONOSPACE = \"monospace\";\n\n    /**\n     * Preferences key for proportional spaced report\n     */\n    private final static String PROPORTIONAL = \"proportional\";\n\n    /**\n     * Preferences key for headers / footers / titles\n     */\n    private final static String HEADER = \"header\";\n\n    private final static String[] DEFAULT_MONO_FONTS = { \"Courier New\", \"Andale Mono\", \"Noto Sans Mono Regular\",\n                    \"Luxi Mono\", \"Liberation Mono\", \"Comic Sans MS\" };\n\n    private final static String[] DEFAULT_PROPORTIONAL_FONTS = { \"Times New Roman\", \"Noto Sans Mono Regular\", \"Luxi Serif\",\n                    \"Liberation Serif\" };\n\n    private final static String[] DEFAULT_HEADER_FONTS = { \"Arial Bold\", \"Noto Sans Bold\", \"Luxi Serif\",\n            \"Liberation Serif\" };\n\n    private ReportFactory() {\n    }\n\n    /**\n     * Returns a report name for a mono spaced, PDF embeddable report\n     * \n     * @return The report name for a mono spaced report\n     */\n    private static String getDefaultMonoFont() {\n\n        final List<String> fonts = FontRegistry.getFontList();\n\n        for (String knownFont : DEFAULT_MONO_FONTS) {\n            if (fonts.contains(knownFont)) {\n                return knownFont; // it found it!\n            }\n        }\n        return \"Monospaced\"; // fail safe\n    }\n\n    /**\n     * Returns a report name for a proportional, PDF embeddable report\n     * \n     * @return The report name for a proportional spaced report\n     */\n    private static String getDefaultProportionalFont() {\n\n        final List<String> fonts = FontRegistry.getFontList();\n\n        for (String knownFont : DEFAULT_PROPORTIONAL_FONTS) {\n            if (fonts.contains(knownFont)) {\n                return knownFont; // it found it!\n            }\n        }\n        return \"SansSerif\"; // fail safe\n    }\n\n    /**\n     * Returns a font name for a proportional, PDF embeddable report\n     *\n     * @return The font name for headers\n     */\n    private static String getDefaultHeaderFont() {\n\n        final List<String> fonts = FontRegistry.getFontList();\n\n        for (String knownFont : DEFAULT_HEADER_FONTS) {\n            if (fonts.contains(knownFont)) {\n                return knownFont; // it found it!\n            }\n        }\n        return \"SansSerif\"; // fail safe\n    }\n\n    /**\n     * Returns the name of the mono spaced font to use\n     * \n     * @return name of the mono spaced font to use\n     */\n    public static String getMonoFont() {\n        Preferences p = Preferences.userNodeForPackage(ReportFactory.class);\n        return p.get(MONOSPACE, getDefaultMonoFont());\n    }\n\n    /**\n     * Returns the name of the proportional spaced font to use\n     * \n     * @return name of the proportional spaced font to use\n     */\n    public static String getProportionalFont() {\n        Preferences p = Preferences.userNodeForPackage(ReportFactory.class);\n        return p.get(PROPORTIONAL, getDefaultProportionalFont());\n    }\n\n    /**\n     * Returns the name of the proportional spaced font to use\n     *\n     * @return name of the proportional spaced font to use\n     */\n    public static String getHeaderFont() {\n        Preferences p = Preferences.userNodeForPackage(ReportFactory.class);\n        return p.get(HEADER, getDefaultHeaderFont());\n    }\n\n    /**\n     * Sets the name of the mono spaced font to use\n     * \n     * @param font report name to use\n     */\n    public static void setMonoFont(final String font) {\n        Preferences p = Preferences.userNodeForPackage(ReportFactory.class);\n        p.put(MONOSPACE, font);\n    }\n\n    /**\n     * Sets the name of the proportional spaced font to use\n     * \n     * @param font font name to use\n     */\n    public static void setProportionalFont(final String font) {\n        Preferences p = Preferences.userNodeForPackage(ReportFactory.class);\n        p.put(PROPORTIONAL, font);\n    }\n\n    /**\n     * Sets the name of the header font to use\n     *\n     * @param font font name to use\n     */\n    public static void setHeaderFont(final String font) {\n        Preferences p = Preferences.userNodeForPackage(ReportFactory.class);\n        p.put(HEADER, font);\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/poi/BudgetResultsExport.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.poi;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.text.DecimalFormat;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountGroup;\nimport jgnash.engine.Comparators;\nimport jgnash.engine.budget.BudgetPeriodResults;\nimport jgnash.engine.budget.BudgetResultsModel;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.util.FileUtils;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.apache.poi.ss.usermodel.CellType;\nimport org.apache.poi.ss.usermodel.CreationHelper;\nimport org.apache.poi.ss.usermodel.DataFormat;\nimport org.apache.poi.ss.usermodel.Font;\nimport org.apache.poi.ss.usermodel.FormulaEvaluator;\nimport org.apache.poi.ss.usermodel.HorizontalAlignment;\nimport org.apache.poi.ss.usermodel.IndexedColors;\nimport org.apache.poi.ss.usermodel.Row;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.apache.poi.ss.util.CellRangeAddress;\nimport org.apache.poi.ss.util.CellReference;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\n\n/**\n * Utility class to export a {@code BudgetResultsModel}.\n *\n * @author Craig Cavanaugh\n */\npublic class BudgetResultsExport {\n\n    private BudgetResultsExport() {\n        // utility class\n    }\n\n    /**\n     * Exports a {@code BudgetResultsModel} to a spreadsheet.\n     * \n     * @param file File to save to\n     * @param model Results model to export\n     * @return Error message\n     */\n    public static String exportBudgetResultsModel(final Path file, final BudgetResultsModel model) {\n        \n        String message = null;\n\n        final ResourceBundle rb = ResourceUtils.getBundle();\n        \n        final String extension = FileUtils.getFileExtension(file.toString());\n        \n        try (final Workbook wb = extension.equals(\"xlsx\") ? new XSSFWorkbook() : new HSSFWorkbook()) {        \t\n        \tfinal CreationHelper createHelper = wb.getCreationHelper();\n\n            // create a new sheet\n            final Sheet s = wb.createSheet(model.getBudget().getName());\n\n            // create header cell styles, override the defaults\n            final CellStyle headerStyle = StyleFactory.createHeaderStyle(wb);\n            headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());\n            final Font headerFont = StyleFactory.createHeaderFont(wb);\n            headerFont.setColor(IndexedColors.BLACK.index);\n            headerStyle.setFont(headerFont);\n\n            // Set the other cell style and formatting\n            final DataFormat df_header = wb.createDataFormat();\n            headerStyle.setDataFormat(df_header.getFormat(\"text\"));\n\n            // create fonts objects\n            final Font amountFont = StyleFactory.createDefaultFont(wb);\n\n            int row = 0;\n            Row r = s.createRow(row);\n\n            // fill the corner\n            Cell corner = r.createCell(0);\n            corner.setCellStyle(headerStyle);\n\n\n            // create period headers\n            for (int i = 0; i < model.getDescriptorList().size(); i++) {\n                Cell c = r.createCell(i * 3 + 1);\n                c.setCellValue(createHelper.createRichTextString(model.getDescriptorList().get(i).getPeriodDescription()));\n                c.setCellStyle(headerStyle);\n                s.addMergedRegion(new CellRangeAddress(row, row, i * 3 + 1, i * 3 + 3));\n            }\n\n            {\n                int col = model.getDescriptorList().size() * 3 + 1;\n                Cell c = r.createCell(col);\n                c.setCellValue(createHelper.createRichTextString(rb.getString(\"Title.Summary\")));\n                c.setCellStyle(headerStyle);\n                s.addMergedRegion(new CellRangeAddress(row, row, col, col + 2));\n            }\n\n            // create results header columns\n            row++;\n            r = s.createRow(row);\n\n            {\n                Cell c = r.createCell(0);\n                c.setCellValue(createHelper.createRichTextString(rb.getString(\"Column.Account\")));\n                c.setCellStyle(headerStyle);\n\n                for (int i = 0; i <= model.getDescriptorList().size(); i++) {\n                    c = r.createCell(i * 3 + 1);\n                    c.setCellValue(createHelper.createRichTextString(rb.getString(\"Column.Budgeted\")));\n                    c.setCellStyle(headerStyle);\n\n                    c = r.createCell(i * 3 + 2);\n                    c.setCellValue(createHelper.createRichTextString(rb.getString(\"Column.Actual\")));\n                    c.setCellStyle(headerStyle);\n\n                    c = r.createCell(i * 3 + 3);\n                    c.setCellValue(createHelper.createRichTextString(rb.getString(\"Column.Remaining\")));\n                    c.setCellStyle(headerStyle);\n                }\n            }\n\n            // must sort the accounts, otherwise child structure is not correct\n            List<Account> accounts = new ArrayList<>(model.getAccounts());\n            accounts.sort(Comparators.getAccountByTreePosition(Comparators.getAccountByCode()));\n\n            // create account rows\n            for (final Account account : accounts) {\n                final CellStyle amountStyle = StyleFactory.createDefaultAmountStyle(wb, account.getCurrencyNode());\n\n                // Sets cell indentation, only impacts display if users changes the cell formatting to be left aligned.\n                amountStyle.setIndention((short) (model.getDepth(account) * 2));\n\n                row++;\n\n                int col = 0;\n\n                r = s.createRow(row);\n\n                CellStyle cs = wb.createCellStyle();\n                cs.cloneStyleFrom(headerStyle);\n                cs.setAlignment(HorizontalAlignment.LEFT);\n                cs.setIndention((short) (model.getDepth(account) * 2));\n\n                Cell c = r.createCell(col);\n                c.setCellValue(createHelper.createRichTextString(account.getName()));\n                c.setCellStyle(cs);\n\n                List<CellReference> budgetedRefList = new ArrayList<>();\n                List<CellReference> changeRefList = new ArrayList<>();\n                List<CellReference> remainingRefList = new ArrayList<>();\n\n                for (int i = 0; i < model.getDescriptorList().size(); i++) {\n\n                    BudgetPeriodResults results = model.getResults(model.getDescriptorList().get(i), account);\n\n                    c = r.createCell(++col, CellType.NUMERIC);\n                    c.setCellValue(results.getBudgeted().doubleValue());\n                    c.setCellStyle(amountStyle);\n\n                    CellReference budgetedRef = new CellReference(row, col);\n                    budgetedRefList.add(budgetedRef);\n\n                    c = r.createCell(++col, CellType.NUMERIC);\n                    c.setCellValue(results.getChange().doubleValue());\n                    c.setCellStyle(amountStyle);\n\n                    CellReference changeRef = new CellReference(row, col);\n                    changeRefList.add(changeRef);\n\n                    c = r.createCell(++col, CellType.FORMULA);\n                    c.setCellStyle(amountStyle);\n                    c.setCellFormula(budgetedRef.formatAsString() + \"-\" + changeRef.formatAsString());\n\n                    CellReference remainingRef = new CellReference(row, col);\n                    remainingRefList.add(remainingRef);\n                }\n\n                // add summary columns                               \n                addSummaryCell(r, ++col, budgetedRefList, amountStyle);\n                addSummaryCell(r, ++col, changeRefList, amountStyle);\n                addSummaryCell(r, ++col, remainingRefList, amountStyle);\n            }\n\n            // add group summary rows\n            for (final AccountGroup group : model.getAccountGroupList()) {\n                final DataFormat df = wb.createDataFormat();\n\n                // reuse the header style but align right\n                final CellStyle amountStyle = StyleFactory.createHeaderStyle(wb);\n                amountStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());\n                amountStyle.setAlignment(HorizontalAlignment.RIGHT);\n                amountStyle.setFont(amountFont);\n\n                final DecimalFormat format = (DecimalFormat) NumericFormats.getFullCommodityFormat(model.getBaseCurrency());\n                final String pattern = format.toLocalizedPattern().replace(\"¤\", model.getBaseCurrency().getPrefix());\n                amountStyle.setDataFormat(df.getFormat(pattern));\n\n                row++;\n\n                int col = 0;\n\n                r = s.createRow(row);\n\n                CellStyle cs = wb.createCellStyle();\n                cs.cloneStyleFrom(headerStyle);\n                cs.setAlignment(HorizontalAlignment.LEFT);\n\n                Cell c = r.createCell(col);\n                c.setCellValue(createHelper.createRichTextString(group.toString()));\n                c.setCellStyle(cs);\n\n                List<CellReference> budgetedRefList = new ArrayList<>();\n                List<CellReference> changeRefList = new ArrayList<>();\n                List<CellReference> remainingRefList = new ArrayList<>();\n\n                for (int i = 0; i < model.getDescriptorList().size(); i++) {\n\n                    BudgetPeriodResults results = model.getResults(model.getDescriptorList().get(i), group);\n\n                    c = r.createCell(++col, CellType.NUMERIC);\n                    c.setCellValue(results.getBudgeted().doubleValue());\n                    c.setCellStyle(amountStyle);\n\n                    CellReference budgetedRef = new CellReference(row, col);\n                    budgetedRefList.add(budgetedRef);\n\n                    c = r.createCell(++col, CellType.NUMERIC);\n                    c.setCellValue(results.getChange().doubleValue());\n                    c.setCellStyle(amountStyle);\n\n                    CellReference changeRef = new CellReference(row, col);\n                    changeRefList.add(changeRef);\n\n                    c = r.createCell(++col, CellType.FORMULA);\n                    c.setCellStyle(amountStyle);\n                    c.setCellFormula(budgetedRef.formatAsString() + \"-\" + changeRef.formatAsString());\n\n                    CellReference remainingRef = new CellReference(row, col);\n                    remainingRefList.add(remainingRef);\n                }\n\n                // add summary columns                               \n                addSummaryCell(r, ++col, budgetedRefList, amountStyle);\n                addSummaryCell(r, ++col, changeRefList, amountStyle);\n                addSummaryCell(r, ++col, remainingRefList, amountStyle);\n            }\n\n            // force evaluation\n            FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();\n            evaluator.evaluateAll();\n\n            short columnCount = s.getRow(1).getLastCellNum();\n\n            // autosize all of the columns + 10 pixels\n            for (int i = 0; i <= columnCount; i++) {\n                s.autoSizeColumn(i);\n                s.setColumnWidth(i, s.getColumnWidth(i) + 10);\n            }\n\n            Logger.getLogger(BudgetResultsExport.class.getName()).log(Level.INFO, \"{0} cell styles were used\", wb.getNumCellStyles());\n\n            // Save\n            String filename = file.toString();\n\n            if (wb instanceof XSSFWorkbook) {\n                filename = FileUtils.stripFileExtension(filename) + \".xlsx\";\n            } else {\n                filename = FileUtils.stripFileExtension(filename) + \".xls\";\n            }\n            \n            try (final OutputStream out = Files.newOutputStream(Paths.get(filename))) {\n                wb.write(out);              \n            } catch (final Exception e) {\n                Logger.getLogger(BudgetResultsExport.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n                message = e.getLocalizedMessage();\n            }        \t        \t\n        } catch (IOException e) {\n        \tLogger.getLogger(BudgetResultsExport.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n\t\t}                      \n        \n        return message;\n    }\n\n    private static void addSummaryCell(final Row row, final int col, final List<CellReference> cellReferenceList, final CellStyle style) {\n        final Cell c = row.createCell(col, CellType.FORMULA);\n        c.setCellStyle(style);\n        c.setCellFormula(buildAddFormula(cellReferenceList));\n    }\n\n    private static String buildAddFormula(final List<CellReference> cellReferenceList) {\n        final StringBuilder formula = new StringBuilder(cellReferenceList.get(0).formatAsString());\n\n        for (int i = 1; i < cellReferenceList.size(); i++) {\n            formula.append('+');\n            formula.append(cellReferenceList.get(i).formatAsString());\n        }\n\n        return formula.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/poi/StyleFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.poi;\n\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.Objects;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.MathConstants;\nimport jgnash.text.NumericFormats;\nimport jgnash.util.NotNull;\n\nimport org.apache.poi.ss.usermodel.BorderStyle;\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.apache.poi.ss.usermodel.DataFormat;\nimport org.apache.poi.ss.usermodel.FillPatternType;\nimport org.apache.poi.ss.usermodel.Font;\nimport org.apache.poi.ss.usermodel.HorizontalAlignment;\nimport org.apache.poi.ss.usermodel.IndexedColors;\nimport org.apache.poi.ss.usermodel.Workbook;\n\n/**\n * Factory class for generating consistent POI styles\n *\n * @author Craig Cavanaugh\n */\nclass StyleFactory {\n\n    static final int DEFAULT_HEIGHT = 10;\n    static final int HEADER_FOOTER_HEIGHT = 11;\n    static final int MARGIN = 4;\n    static final int TITLE_HEIGHT = 14;\n    static final int GROUP_HEIGHT = 12;\n\n    private StyleFactory() {\n        // Utility class\n    }\n\n    /**\n     * Creates the default header style\n     *\n     * @param wb {@code Workbook} the new style is to be assigned to\n     * @return a new {@code CellStyle} instance\n     */\n    static CellStyle createFooterStyle(final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final CellStyle footerStyle = wb.createCellStyle();\n        footerStyle.setBorderBottom(BorderStyle.THIN);\n        footerStyle.setBorderTop(BorderStyle.THIN);\n        footerStyle.setBorderLeft(BorderStyle.THIN);\n        footerStyle.setBorderRight(BorderStyle.THIN);\n        footerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());\n        footerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);\n        footerStyle.setAlignment(HorizontalAlignment.RIGHT);\n        footerStyle.setFont(createFooterFont(wb));\n\n        return footerStyle;\n    }\n\n    /**\n     * Creates the default header style\n     *\n     * @param wb {@code Workbook} the new style is to be assigned to\n     * @return a new {@code CellStyle} instance\n     */\n    static CellStyle createHeaderStyle(final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final CellStyle headerStyle = wb.createCellStyle();\n        headerStyle.setBorderBottom(BorderStyle.THIN);\n        headerStyle.setBorderTop(BorderStyle.THIN);\n        headerStyle.setBorderLeft(BorderStyle.THIN);\n        headerStyle.setBorderRight(BorderStyle.THIN);\n        headerStyle.setFillForegroundColor(IndexedColors.BLACK.getIndex());\n        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);\n        headerStyle.setAlignment(HorizontalAlignment.CENTER);\n        headerStyle.setFont(createHeaderFont(wb));\n\n        return headerStyle;\n    }\n\n    /**\n     * Applies a currency format to a {@code CellStyle}\n     *\n     * @param wb           the {@code Workbook} the numeric format is being created for\n     * @param currencyNode the {@code CurrencyNode} to extract symbol information from\n     * @param cellStyle    the {@code CellStyle} being updated\n     * @return the {@code CellStyle} being updated\n     */\n    static CellStyle applyCurrencyFormat(final Workbook wb, final CurrencyNode currencyNode, final CellStyle cellStyle) {\n        final DecimalFormat format = (DecimalFormat) NumericFormats.getFullCommodityFormat(currencyNode);\n        final String pattern = format.toLocalizedPattern().replace(\"¤\", currencyNode.getPrefix());\n        final DataFormat df = wb.createDataFormat();\n        cellStyle.setDataFormat(df.getFormat(pattern));\n        cellStyle.setAlignment(HorizontalAlignment.RIGHT);\n\n        return cellStyle;\n    }\n\n    /**\n     * Applies a percentage format to a {@code CellStyle}\n     *\n     * @param wb        the {@code Workbook} the numeric format is being created for\n     * @param cellStyle the {@code CellStyle} being updated\n     * @return the {@code CellStyle} being updated\n     */\n    static CellStyle applyPercentageFormat(final Workbook wb, final CellStyle cellStyle) {\n\n        final NumberFormat percentageFormat = NumericFormats.getPercentageFormat();\n\n        final DataFormat df = wb.createDataFormat();\n        cellStyle.setDataFormat(df.getFormat(((DecimalFormat) percentageFormat).toPattern()));\n        cellStyle.setAlignment(HorizontalAlignment.RIGHT);\n\n        return cellStyle;\n    }\n\n    /**\n     * Applies a Security quantity format to a {@code CellStyle}\n     *\n     * @param wb        the {@code Workbook} the numeric format is being created for\n     * @param cellStyle the {@code CellStyle} being updated\n     * @return the {@code CellStyle} being updated\n     */\n    static CellStyle applySecurityQuantityFormat(final Workbook wb, final CellStyle cellStyle) {\n\n        final NumberFormat qtyFormat = NumericFormats.getFixedPrecisionFormat(MathConstants.SECURITY_QUANTITY_ACCURACY);\n\n        final DataFormat df = wb.createDataFormat();\n        cellStyle.setDataFormat(df.getFormat(((DecimalFormat) qtyFormat).toPattern()));\n        cellStyle.setAlignment(HorizontalAlignment.RIGHT);\n\n        return cellStyle;\n    }\n\n    /**\n     * Applies a short date format to a {@code CellStyle}\n     *\n     * @param wb        the {@code Workbook} the numeric format is being created for\n     * @param cellStyle the {@code CellStyle} being updated\n     * @return the {@code CellStyle} being updated\n     */\n    static CellStyle applyShortDateFormat(final Workbook wb, final CellStyle cellStyle) {\n        cellStyle.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(\"mm/dd/yy\"));\n        cellStyle.setAlignment(HorizontalAlignment.LEFT);\n\n        return cellStyle;\n    }\n\n    /**\n     * Applies a short date format to a {@code CellStyle}\n     *\n     * @param wb        the {@code Workbook} the numeric format is being created for\n     * @param cellStyle the {@code CellStyle} being updated\n     * @return the {@code CellStyle} being updated\n     */\n    static CellStyle applyTimestampFormat(final Workbook wb, final CellStyle cellStyle) {\n        cellStyle.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat(\"YYYY-MM-DD HH:MM:SS\"));\n        cellStyle.setAlignment(HorizontalAlignment.LEFT);\n\n        return cellStyle;\n    }\n\n    /**\n     * Creates the default {@code CellStyle} for currency value\n     *\n     * @param wb the {@code Workbook} the numeric format is being created for\n     * @return the {@code CellStyle} being created\n     */\n    static CellStyle createDefaultAmountStyle(@NotNull final Workbook wb, @NotNull final CurrencyNode currencyNode) {\n        return applyCurrencyFormat(wb, currencyNode, createDefaultStyle(wb));\n    }\n\n    /**\n     * Creates the default {@code CellStyle} for a {@code Workbook}\n     *\n     * @param wb the {@code Workbook} the default format is being created for\n     * @return the {@code CellStyle} being created\n     */\n    static CellStyle createDefaultStyle(@NotNull final Workbook wb) {\n        final Font defaultFont = createDefaultFont(wb);\n        final CellStyle cellStyle = wb.createCellStyle();\n\n        cellStyle.setFont(defaultFont);\n\n        return cellStyle;\n    }\n\n    /**\n     * Creates the Title {@code CellStyle} for a {@code Workbook}\n     *\n     * @param wb the {@code Workbook} the default format is being created for\n     * @return the {@code CellStyle} being created\n     */\n    static CellStyle createTitleStyle(@NotNull final Workbook wb) {\n        final Font font = createTitleFont(wb);\n        final CellStyle cellStyle = wb.createCellStyle();\n\n        cellStyle.setFont(font);\n        cellStyle.setAlignment(HorizontalAlignment.CENTER);\n\n        return cellStyle;\n    }\n\n    /**\n     * Creates the Title {@code CellStyle} for a {@code Workbook}\n     *\n     * @param wb the {@code Workbook} the default format is being created for\n     * @return the {@code CellStyle} being created\n     */\n    static CellStyle createGroupStyle(@NotNull final Workbook wb) {\n        final Font font = createGroupFont(wb);\n        final CellStyle cellStyle = wb.createCellStyle();\n\n        cellStyle.setFont(font);\n        cellStyle.setAlignment(HorizontalAlignment.LEFT);\n\n        return cellStyle;\n    }\n\n    /**\n     * Creates the Title {@code CellStyle} for a {@code Workbook}\n     *\n     * @param wb the {@code Workbook} the default format is being created for\n     * @return the {@code CellStyle} being created\n     */\n    static CellStyle createSubTitleStyle(@NotNull final Workbook wb) {\n        final Font font = createSubTitleFont(wb);\n        final CellStyle cellStyle = wb.createCellStyle();\n\n        cellStyle.setFont(font);\n        cellStyle.setAlignment(HorizontalAlignment.CENTER);\n\n        return cellStyle;\n    }\n\n    /**\n     * Creates the default header font\n     *\n     * @param wb {@code Workbook} font is to be assigned to\n     * @return a new {@code Font} instance\n     */\n    private static Font createFooterFont(@NotNull final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final Font font = wb.createFont();\n        font.setFontHeightInPoints((short) HEADER_FOOTER_HEIGHT);\n        font.setColor(IndexedColors.BLACK.getIndex());\n        font.setBold(true);\n\n        return font;\n    }\n\n    /**\n     * Creates the default header font\n     *\n     * @param wb {@code Workbook} font is to be assigned to\n     * @return a new {@code Font} instance\n     */\n    static Font createHeaderFont(@NotNull final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final Font font = wb.createFont();\n        font.setFontHeightInPoints((short) HEADER_FOOTER_HEIGHT);\n        font.setColor(IndexedColors.WHITE.getIndex());\n        font.setBold(true);\n\n        return font;\n    }\n\n    /**\n     * Creates the default font\n     *\n     * @param wb {@code Workbook} font is to be assigned to\n     * @return a new {@code Font} instance\n     */\n    static Font createDefaultFont(@NotNull final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final Font font = wb.createFont();\n        font.setFontHeightInPoints((short) DEFAULT_HEIGHT);\n        font.setColor(IndexedColors.BLACK.getIndex());\n\n        return font;\n    }\n\n    /**\n     * Creates the default title font\n     *\n     * @param wb {@code Workbook} font is to be assigned to\n     * @return a new {@code Font} instance\n     */\n    private static Font createTitleFont(@NotNull final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final Font font = wb.createFont();\n        font.setFontHeightInPoints((short) TITLE_HEIGHT);\n        font.setBold(true);\n        font.setColor(IndexedColors.BLACK.getIndex());\n\n        return font;\n    }\n\n    /**\n     * Creates the default group font\n     *\n     * @param wb {@code Workbook} font is to be assigned to\n     * @return a new {@code Font} instance\n     */\n    private static Font createGroupFont(@NotNull final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final Font font = wb.createFont();\n        font.setFontHeightInPoints((short) GROUP_HEIGHT);\n        font.setBold(true);\n        font.setColor(IndexedColors.BLACK.getIndex());\n\n        return font;\n    }\n\n    /**\n     * Creates the default title font\n     *\n     * @param wb {@code Workbook} font is to be assigned to\n     * @return a new {@code Font} instance\n     */\n    private static Font createSubTitleFont(@NotNull final Workbook wb) {\n        Objects.requireNonNull(wb);\n\n        final Font font = createDefaultFont(wb);\n        font.setItalic(true);\n\n        return font;\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/poi/Workbook.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.poi;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.util.EnumMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.report.table.GroupInfo;\nimport jgnash.time.DateUtils;\nimport jgnash.util.FileUtils;\nimport jgnash.util.NotNull;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.apache.poi.ss.usermodel.CellType;\nimport org.apache.poi.ss.usermodel.CreationHelper;\nimport org.apache.poi.ss.usermodel.Row;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.util.CellRangeAddress;\nimport org.apache.poi.ss.util.WorkbookUtil;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\n\nimport static jgnash.report.poi.StyleFactory.DEFAULT_HEIGHT;\nimport static jgnash.report.poi.StyleFactory.GROUP_HEIGHT;\nimport static jgnash.report.poi.StyleFactory.HEADER_FOOTER_HEIGHT;\nimport static jgnash.report.poi.StyleFactory.MARGIN;\nimport static jgnash.report.poi.StyleFactory.TITLE_HEIGHT;\n\n/**\n * Exports a {@code AbstractReportTableModel} to a spreadsheet using POI\n *\n * TODO: Cross tabulation formulas\n *\n * @author Craig Cavanaugh\n */\npublic class Workbook {\n\n    private Workbook() {\n        // utility class\n    }\n\n    public static void export(@NotNull final AbstractReportTableModel reportModel, @NotNull final File file) {\n        Objects.requireNonNull(reportModel);\n        Objects.requireNonNull(file);\n\n        final Logger logger = Logger.getLogger(Workbook.class.getName());\n\n        final String extension = FileUtils.getFileExtension(file.getAbsolutePath());\n\n        try (final org.apache.poi.ss.usermodel.Workbook wb = extension.equals(\"xlsx\") ? new XSSFWorkbook() : new HSSFWorkbook()) {\n\n            final Map<Style, CellStyle> styleMap = buildStyleMap(wb, reportModel.getCurrencyNode());\n\n            // create a new sheet\n            final Sheet sheet = wb.createSheet(WorkbookUtil.createSafeSheetName(\"Sheet1\"));\n\n            final Set<GroupInfo> groupInfoSet = GroupInfo.getGroups(reportModel);\n\n            int sheetRow = 0;\n\n            // Add the title\n            sheetRow = addReportHeader(reportModel, styleMap, wb, sheet, sheetRow);\n\n            // write all of the groups\n            for (final GroupInfo groupInfo : groupInfoSet) {\n                sheetRow = addTableSection(reportModel, styleMap, wb, sheet, groupInfo, sheetRow);\n            }\n\n            // Add global footer column\n            if (reportModel.hasGlobalSummary()) {\n                addGlobalFooter(reportModel, styleMap, wb, sheet, sheetRow);\n            }\n\n\n            // autosize the columns\n            int col = 0;\n            for (int c = 0; c < reportModel.getColumnCount(); c++) {\n                if (reportModel.isColumnVisible(c)) {\n                    sheet.autoSizeColumn(col);\n                    sheet.setColumnWidth(col, sheet.getColumnWidth(col) + 10);\n\n                    col++;\n                }\n            }\n\n            logger.log(Level.INFO, \"{0} cell styles were used\", wb.getNumCellStyles());\n\n            // Save the file\n            final String filename;\n\n            if (wb instanceof XSSFWorkbook) {\n                filename = FileUtils.stripFileExtension(file.getAbsolutePath()) + \".xlsx\";\n            } else {\n                filename = FileUtils.stripFileExtension(file.getAbsolutePath()) + \".xls\";\n            }\n\n            try (final OutputStream out = Files.newOutputStream(Paths.get(filename))) {\n                wb.write(out);\n            } catch (final Exception e) {\n                logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            }\n\n        } catch (final IOException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n        }\n    }\n\n    private static int addReportHeader(@NotNull final AbstractReportTableModel reportModel,\n                                       @NotNull final Map<Style, CellStyle> styleMap,\n                                       @NotNull final org.apache.poi.ss.usermodel.Workbook wb, @NotNull final Sheet s,\n                                       final int startRow) {\n\n        int sheetRow = startRow;\n        final CreationHelper createHelper = wb.getCreationHelper();\n\n        Row row = s.createRow(sheetRow);\n\n        Cell cell = row.createCell(0);\n        cell.setCellStyle(styleMap.get(Style.TITLE));\n        cell.setCellValue(createHelper.createRichTextString(reportModel.getTitle()));\n        s.addMergedRegion(new CellRangeAddress(sheetRow, sheetRow, 0, reportModel.getVisibleColumnCount() - 1));\n\n        row.setHeightInPoints(TITLE_HEIGHT + MARGIN);\n\n        sheetRow++;\n\n        if (reportModel.getSubTitle() != null && !reportModel.getSubTitle().isBlank()) {\n            row = s.createRow(sheetRow);\n\n            cell = row.createCell(0);\n            cell.setCellStyle(styleMap.get(Style.SUBTITLE));\n            cell.setCellValue(createHelper.createRichTextString(reportModel.getSubTitle()));\n            s.addMergedRegion(new CellRangeAddress(sheetRow, sheetRow, 0, reportModel.getVisibleColumnCount() - 1));\n\n            row.setHeightInPoints(DEFAULT_HEIGHT + MARGIN);\n\n            sheetRow++;\n        }\n\n        return ++sheetRow;\n    }\n\n    private static int addTableSection(@NotNull final AbstractReportTableModel reportModel, @NotNull final Map<Style, CellStyle> styleMap,\n                                       @NotNull final org.apache.poi.ss.usermodel.Workbook wb, @NotNull final Sheet s,\n                                       @NotNull final GroupInfo groupInfo, final int startRow) {\n\n        final CellStyle headerStyle = styleMap.get(Style.HEADER);\n\n        final CreationHelper createHelper = wb.getCreationHelper();\n\n        final String group = groupInfo.group;\n\n        int sheetRow = startRow;\n\n        int col = 0; // reusable col tracker\n\n        Row row;\n\n        Cell cell;\n\n        // Add group title\n        if (!group.equals(AbstractReportTableModel.DEFAULT_GROUP) ) {\n            row = s.createRow(sheetRow);\n            cell = row.createCell(col);\n            cell.setCellStyle(styleMap.get(Style.GROUP));\n            cell.setCellValue(createHelper.createRichTextString(group));\n            s.addMergedRegion(new CellRangeAddress(sheetRow, sheetRow, 0, reportModel.getVisibleColumnCount() - 1));\n            row.setHeightInPoints(GROUP_HEIGHT + MARGIN);\n\n            sheetRow++;\n        }\n\n        // Create headers\n        row = s.createRow(sheetRow);\n        row.setHeightInPoints(HEADER_FOOTER_HEIGHT + MARGIN);\n\n        col = 0; // reusable col tracker\n\n        for (int c = 0; c < reportModel.getColumnCount(); c++) {\n            if (reportModel.isColumnVisible(c)) {\n                cell = row.createCell(col);\n\n                cell.setCellStyle(headerStyle);\n                cell.setCellValue(createHelper.createRichTextString(reportModel.getColumnName(c)));\n\n                col++;\n            }\n        }\n\n        sheetRow++;\n\n        // add the groups rows\n        for (int tableRow = 0; tableRow < reportModel.getRowCount(); tableRow++) {\n            final String rowGroup = reportModel.getGroup(tableRow);\n\n            col = 0;\n\n            if (group.equals(rowGroup)) {\n                row = s.createRow(sheetRow);   // new row is needed\n                row.setHeightInPoints(DEFAULT_HEIGHT + MARGIN);\n\n                for (int tableCol = 0; tableCol < reportModel.getColumnCount(); tableCol++) {\n                    if (reportModel.isColumnVisible(tableCol)) {\n                        setCellValue(reportModel, styleMap, wb, row, col, tableRow, tableCol);\n                        col++;\n                    }\n                }\n                sheetRow++;\n            }\n        }\n\n        // add the group footer if needed\n        if (groupInfo.hasSummation()) {\n            col = 0;\n            row = s.createRow(sheetRow);   // new row is needed\n            row.setHeightInPoints(HEADER_FOOTER_HEIGHT + MARGIN);\n\n            // column zero is assumed to be a total descriptor\n            setFooterCellValue(reportModel.getGroupFooterLabel(), ColumnStyle.STRING, styleMap, wb, row, col);\n\n            col++;\n\n            for (int c = 1; c < reportModel.getColumnCount(); c++) {\n                if (reportModel.isColumnVisible(c)) {\n                    setFooterCellValue(groupInfo.getValue(c), reportModel.getColumnStyle(c), styleMap, wb, row, col);\n\n                    col++;\n                }\n            }\n\n            sheetRow++;\n        }\n\n        return ++sheetRow;\n    }\n\n    private static void addGlobalFooter(@NotNull final AbstractReportTableModel reportModel, @NotNull final Map<Style, CellStyle> styleMap,\n                                        @NotNull final org.apache.poi.ss.usermodel.Workbook wb, @NotNull final Sheet s,\n                                        final int startRow) {\n        int col = 0;\n        Row row = s.createRow(startRow);   // new row is needed\n        row.setHeightInPoints(HEADER_FOOTER_HEIGHT + MARGIN);\n\n        // column zero is assumed to be a total descriptor\n        setFooterCellValue(reportModel.getGrandTotalLegend(), ColumnStyle.STRING, styleMap, wb, row, col);\n\n        col++;\n\n        for (int c = 1; c < reportModel.getColumnCount(); c++) {\n            if (reportModel.isColumnVisible(c)) {\n                setFooterCellValue(reportModel.getGlobalSum(c), reportModel.getColumnStyle(c), styleMap, wb, row, col);\n\n                col++;\n            }\n        }\n    }\n\n    private static void setCellValue(@NotNull final AbstractReportTableModel reportModel, @NotNull final Map<Style, CellStyle> styleMap,\n                                     @NotNull final org.apache.poi.ss.usermodel.Workbook wb, @NotNull final Row row, final int wbCol,\n                                     final int tableRow, final int tableColumn) {\n\n        if (reportModel.getValueAt(tableRow, tableColumn) != null) {\n            final ColumnStyle columnStyle = reportModel.getColumnStyle(tableColumn);\n\n            switch (columnStyle) {\n                case SHORT_AMOUNT:\n                case BALANCE:\n                case BALANCE_WITH_SUM:\n                case BALANCE_WITH_SUM_AND_GLOBAL:\n                case AMOUNT_SUM: {\n                    Cell cell = row.createCell(wbCol, CellType.NUMERIC);\n                    cell.setCellStyle(styleMap.get(Style.AMOUNT));\n                    cell.setCellValue(((BigDecimal) reportModel.getValueAt(tableRow, tableColumn)).doubleValue());\n                }\n                break;\n                case PERCENTAGE: {\n                    Cell cell = row.createCell(wbCol, CellType.NUMERIC);\n                    cell.setCellStyle(styleMap.get(Style.PERCENTAGE));\n                    cell.setCellValue(((BigDecimal) reportModel.getValueAt(tableRow, tableColumn)).doubleValue());\n                }\n                break;\n                case QUANTITY: {\n                    Cell cell = row.createCell(wbCol, CellType.NUMERIC);\n                    cell.setCellStyle(styleMap.get(Style.QUANTITY));\n                    cell.setCellValue(((BigDecimal) reportModel.getValueAt(tableRow, tableColumn)).doubleValue());\n                }\n                break;\n                case SHORT_DATE: {\n                    Cell cell = row.createCell(wbCol, CellType.STRING);\n                    cell.setCellStyle(styleMap.get(Style.SHORT_DATE));\n                    cell.setCellValue(DateUtils.asDate((LocalDate) reportModel.getValueAt(tableRow, tableColumn)));\n                }\n                break;\n                case TIMESTAMP: {\n                    Cell cell = row.createCell(wbCol, CellType.STRING);\n                    cell.setCellStyle(styleMap.get(Style.TIMESTAMP));\n                    cell.setCellValue(DateUtils.asDate((LocalDateTime) reportModel.getValueAt(tableRow, tableColumn)));\n                }\n                break;\n                case STRING:\n                default: {\n                    final CreationHelper createHelper = wb.getCreationHelper();\n\n                    Cell cell = row.createCell(wbCol);\n                    cell.setCellStyle(styleMap.get(Style.DEFAULT));\n                    cell.setCellValue(createHelper.createRichTextString(reportModel.getValueAt(tableRow, tableColumn).toString()));\n                }\n            }\n        }\n    }\n\n    private static void setFooterCellValue(@NotNull final Object value, @NotNull ColumnStyle columnStyle, @NotNull final Map<Style, CellStyle> styleMap,\n                                           @NotNull final org.apache.poi.ss.usermodel.Workbook wb, @NotNull final Row row,\n                                           final int col) {\n\n        switch (columnStyle) {\n            case AMOUNT_SUM:\n            case BALANCE_WITH_SUM:\n            case BALANCE_WITH_SUM_AND_GLOBAL: {\n                Cell cell = row.createCell(col, CellType.NUMERIC);\n                cell.setCellStyle(styleMap.get(Style.NUMERIC_FOOTER));\n                cell.setCellValue(((BigDecimal) value).doubleValue());\n            }\n            break;\n            case BALANCE:\n            case PERCENTAGE:\n            case QUANTITY: {\n                Cell cell = row.createCell(col);\n                cell.setCellStyle(styleMap.get(Style.FOOTER));\n            }\n            break;\n            case STRING:\n            default: {\n                final CreationHelper createHelper = wb.getCreationHelper();\n\n                Cell cell = row.createCell(col);\n                cell.setCellStyle(styleMap.get(Style.FOOTER));\n\n                cell.setCellValue(createHelper.createRichTextString(value.toString()));\n            }\n        }\n    }\n\n    private static Map<Style, CellStyle> buildStyleMap(final org.apache.poi.ss.usermodel.Workbook wb, final CurrencyNode currencyNode) {\n        final Map<Style, CellStyle> styleMap = new EnumMap<>(Style.class);\n\n        styleMap.put(Style.AMOUNT, StyleFactory.createDefaultAmountStyle(wb, currencyNode));\n        styleMap.put(Style.DEFAULT, StyleFactory.createDefaultStyle(wb));\n        styleMap.put(Style.FOOTER, StyleFactory.createFooterStyle(wb));\n        styleMap.put(Style.GROUP, StyleFactory.createGroupStyle(wb));\n        styleMap.put(Style.HEADER, StyleFactory.createHeaderStyle(wb));\n\n        styleMap.put(Style.NUMERIC_FOOTER, StyleFactory.applyCurrencyFormat(wb, currencyNode,\n                StyleFactory.createFooterStyle(wb)));\n\n        styleMap.put(Style.QUANTITY, StyleFactory.applySecurityQuantityFormat(wb, StyleFactory.createDefaultStyle(wb)));\n\n        styleMap.put(Style.PERCENTAGE, StyleFactory.applyPercentageFormat(wb, StyleFactory.createDefaultStyle(wb)));\n\n        styleMap.put(Style.SHORT_DATE, StyleFactory.applyShortDateFormat(wb, StyleFactory.createDefaultStyle(wb)));\n        styleMap.put(Style.SUBTITLE, StyleFactory.createSubTitleStyle(wb));\n        styleMap.put(Style.TIMESTAMP, StyleFactory.applyTimestampFormat(wb, StyleFactory.createDefaultStyle(wb)));\n        styleMap.put(Style.TITLE, StyleFactory.createTitleStyle(wb));\n\n        return styleMap;\n    }\n\n    private enum Style {\n        AMOUNT,\n        DEFAULT,\n        FOOTER,\n        GROUP,\n        HEADER,\n        NUMERIC_FOOTER,\n        PERCENTAGE,\n        QUANTITY,\n        SHORT_DATE,\n        SUBTITLE,\n        TIMESTAMP,\n        TITLE\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/table/AbstractReportTableModel.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.table;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.MathConstants;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.text.NumericFormats;\nimport jgnash.time.DateUtils;\nimport jgnash.util.LogUtil;\nimport jgnash.util.NotNull;\nimport jgnash.util.Nullable;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\n\n/**\n * Base Report model class\n * <p>\n * The report must contain a minimum of one group defined by {@code ColumnStyle.GROUP_NO_HEADER} or\n * {@code ColumnStyle.GROUP}.  If a group is not defined/assigned, all rows will be grouped together.\n *\n * @author Craig Cavanaugh\n */\npublic abstract class AbstractReportTableModel {\n\n    /**\n     * Default group for unassigned rows.\n     */\n    public static final String DEFAULT_GROUP = \"_default_\";\n\n    public abstract CurrencyNode getCurrencyNode();\n\n    /**\n     * Returns the formatting style for the values in the column.\n     *\n     * @param columnIndex the index of the column\n     * @return the common {@code ColumnStyle} of the object values for the column\n     */\n    public abstract ColumnStyle getColumnStyle(int columnIndex);\n\n    /**\n     * Returns the column class for the values in the column.\n     *\n     * @param columnIndex the index of the column\n     * @return the common  class of the object values in the model.\n     */\n    public abstract Class<?> getColumnClass(int columnIndex);\n\n    /**\n     * Returns the column count in the model.\n     *\n     * @return the number of columns in the model\n     * @see #getRowCount\n     */\n    public abstract int getColumnCount();\n\n    /**\n     * Returns the column width weighting.\n     *\n     * Sum of all flexible columns should sum to 100\n     *\n     * @param columnIndex column index\n     * @return column width weighting\n     */\n    public float getColumnWidthWeight(int columnIndex) {\n        int flexColumnCount = 0;\n\n        for (int i = 0; i < getColumnCount(); i++) {\n            if (!isColumnFixedWidth(i) && isColumnVisible(i)) {\n                flexColumnCount++;\n            }\n        }\n\n        return 100f / flexColumnCount;\n    }\n\n    /**\n     * Returns column name at {@code columnIndex}.\n     *\n     * @param columnIndex the index of the column\n     * @return the name of the column\n     */\n    public abstract String getColumnName(int columnIndex);\n\n    /**\n     * Returns the row count in the model.\n     *\n     * @return the number of rows in the model\n     * @see #getColumnCount\n     */\n    public abstract int getRowCount();\n\n    /**\n     * Returns the value at {@code columnIndex} and {@code rowIndex}.\n     *\n     * @param rowIndex    the row whose value is to be queried\n     * @param columnIndex the column whose value is to be queried\n     * @return the value Object at the specified cell\n     */\n    public abstract Object getValueAt(int rowIndex, int columnIndex);\n\n    /**\n     * Returns the title for the Report\n     *\n     * @return the report title\n     */\n    @NotNull\n    public abstract String getTitle();\n\n    /**\n     * Returns the subtitle for the Report\n     *\n     * @return the report title\n     */\n    @Nullable\n    public abstract String getSubTitle();\n\n    /**\n     * Return true if the column should be fixed width\n     *\n     * @param columnIndex column to verify\n     * @return true if fixed width\n     */\n    public boolean isColumnFixedWidth(final int columnIndex) {\n        return false;\n    }\n\n    private int getGroupColumn() {\n        int column = -1;\n\n        for (int i = 0; i < getColumnCount(); i++) {\n            if (getColumnStyle(i) == ColumnStyle.GROUP) {\n                column = i;\n                break;\n            }\n        }\n        return column;\n    }\n\n    public String getGroup(final int row) {\n        String group = DEFAULT_GROUP;   // default group if row is not assigned\n\n        for (int i = 0; i < getColumnCount(); i++) {\n            if (getColumnStyle(i) == ColumnStyle.GROUP || getColumnStyle(i) == ColumnStyle.GROUP_NO_HEADER) {\n                group = getValueAt(row, i).toString();\n            }\n        }\n\n        return group;\n    }\n\n    @NotNull\n    public int[] getColumnsToHide() {\n        return new int[0];  // return an empty array by default\n    }\n\n    /**\n     * Determines if a column is visible.\n     *\n     * @param column column to check\n     * @return true if the column data is visible in the report\n     */\n    public boolean isColumnVisible(final int column) {\n        boolean result = true;\n\n        final ColumnStyle columnStyle = getColumnStyle(column);\n\n        if (columnStyle == ColumnStyle.GROUP_NO_HEADER || columnStyle == ColumnStyle.GROUP) {\n            result = false;\n        } else {\n            int[] columnsToHide = getColumnsToHide();\n\n            for (int i : columnsToHide) {\n                if (column == i) {\n                    result = false;\n                    break;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Determines if a column should be summed\n     *\n     * @param columnIndex column index to check\n     * @return true if the column values should be summed\n     */\n    public boolean isColumnSummed(final int columnIndex) {\n        return getColumnStyle(columnIndex) == ColumnStyle.BALANCE_WITH_SUM ||\n                       getColumnStyle(columnIndex) == ColumnStyle.BALANCE_WITH_SUM_AND_GLOBAL\n                       || getColumnStyle(columnIndex) == ColumnStyle.AMOUNT_SUM;\n    }\n\n    /**\n     * Returns the column count in the model.\n     *\n     * @return the number of columns in the model\n     * @see #getColumnCount\n     */\n    public int getVisibleColumnCount() {\n\n        int count = 0;\n\n        for (int i = 0; i < getColumnCount(); i++) {\n            if (isColumnVisible(i)) {\n                count++;\n            }\n        }\n\n        return count;\n    }\n\n    private boolean isColumnGloballySummed(final int columnIndex) {\n        return getColumnStyle(columnIndex) == ColumnStyle.BALANCE_WITH_SUM_AND_GLOBAL;\n    }\n\n    public boolean hasGlobalSummary() {\n        boolean result = false;\n\n        for (int i = 0; i < getColumnCount(); i++) {\n            if (isColumnGloballySummed(i)) {\n                result = true;\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    public BigDecimal getGlobalSum(final int columnIndex) {\n        BigDecimal sum = BigDecimal.ZERO;\n\n        if (getColumnClass(columnIndex).isAssignableFrom(BigDecimal.class)) {\n\n            for (int i = 0; i < getRowCount(); i++) {\n                sum = sum.add((BigDecimal) getValueAt(i, columnIndex));\n            }\n        }\n\n        return sum;\n    }\n\n    /**\n     * Returns the legend for the grand total\n     *\n     * @return report name\n     */\n    public String getGrandTotalLegend() {\n        return ResourceUtils.getString(\"Word.Total\");\n    }\n\n    /**\n     * Returns the general label for the group footer\n     *\n     * @return footer label\n     */\n    public String getGroupFooterLabel() {\n        return ResourceUtils.getString(\"Word.Subtotal\");\n    }\n\n    /**\n     * Returns the longest value for the specified column\n     *\n     * @param columnIndex column to check\n     * @return String representing the longest value\n     */\n    public String getColumnPrototypeValueAt(final int columnIndex) {\n\n        final int groupColumn = getGroupColumn();\n\n        String longest = \"\";\n\n        if (getColumnClass(columnIndex).isAssignableFrom(BigDecimal.class)) {\n\n            // does the column need to be summed\n            boolean sum = isColumnSummed(columnIndex);\n\n            // mapping to sum groups\n            final HashMap<String, BigDecimal> groupMap = new HashMap<>();\n\n            // number format to use\n            NumberFormat nf;\n\n            switch (getColumnStyle(columnIndex)) {\n                case QUANTITY:\n                    nf = NumericFormats.getFixedPrecisionFormat(MathConstants.SECURITY_QUANTITY_ACCURACY);\n                    break;\n                case PERCENTAGE:\n                    nf = NumericFormats.getPercentageFormat();\n                    break;\n                default:\n                    nf = NumericFormats.getFullCommodityFormat(getCurrencyNode());\n                    break;\n            }\n\n            BigDecimal total = BigDecimal.ZERO; // end total\n\n            for (int i = 0; i < getRowCount(); i++) {\n                final BigDecimal value = (BigDecimal) getValueAt(i, columnIndex);\n\n                if (value != null) {\n\n                    final String prototype = nf.format(value);\n\n                    // look and individual values\n                    if (prototype.length() > longest.length()) {\n                        longest = prototype;\n                    }\n\n                    if (sum) {\n                        total = total.add(value); // global value\n                    }\n\n                    if (groupColumn >= 0) {\n                        final String group = (String) getValueAt(i, groupColumn);\n                        final BigDecimal o = groupMap.get(group);\n\n                        groupMap.put(group, o != null ? o.add(value) : value);\n                    }\n                }\n            }\n\n            if (sum) {\n                if (nf.format(total).length() > longest.length()) { // look at column total\n                    longest = nf.format(total);\n                }\n\n                for (final BigDecimal value : groupMap.values()) {\n                    if (nf.format(value).length() > longest.length()) { // look at group totals\n                        longest = nf.format(value);\n                    }\n                }\n            }\n\n            if (isColumnGloballySummed(columnIndex)) {\n                final BigDecimal globalSum = getGlobalSum(columnIndex);\n\n                if (nf.format(globalSum).length() > longest.length()) { // look at group totals\n                    longest = nf.format(globalSum);\n                }\n            }\n\n        } else if (getColumnStyle(columnIndex) == ColumnStyle.STRING) {\n            for (int i = 0; i < getRowCount(); i++) {\n                final String val = (String) getValueAt(i, columnIndex);\n\n                if (val != null && val.length() > longest.length()) {\n                    longest = val;\n                }\n            }\n        } else if (getColumnStyle(columnIndex) == ColumnStyle.SHORT_DATE) {\n            final DateTimeFormatter dateTimeFormatter = DateUtils.getShortDateFormatter();\n\n            for (int i = 0; i < getRowCount(); i++) {\n                try {\n                    final LocalDate date = (LocalDate) getValueAt(i, columnIndex);\n\n                    if (date != null) {\n                        final String val = dateTimeFormatter.format(date);\n                        if (val.length() > longest.length()) {\n                            longest = val;\n                        }\n                    }\n                } catch (final IllegalArgumentException e) {\n                    LogUtil.logSevere(AbstractReportTableModel.class, e);\n                }\n            }\n        } else if (getColumnStyle(columnIndex) == ColumnStyle.TIMESTAMP) {\n            final DateTimeFormatter dateTimeFormatter = DateUtils.getShortDateTimeFormatter();\n\n            for (int i = 0; i < getRowCount(); i++) {\n                try {\n                    final LocalDateTime localDateTime = (LocalDateTime) getValueAt(i, columnIndex);\n\n                    if (localDateTime != null) {\n                        final String val = dateTimeFormatter.format(localDateTime);\n                        if (val.length() > longest.length()) {\n                            longest = val;\n                        }\n                    }\n                } catch (final IllegalArgumentException e) {\n                    LogUtil.logSevere(AbstractReportTableModel.class, e);\n                }\n            }\n        }\n\n        return longest;\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/table/ColumnStyle.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.table;\n\n/**\n * Available report column styles\n *\n * @author Craig Cavanaugh\n */\npublic enum ColumnStyle {\n    AMOUNT_SUM,\n    BALANCE,\n    BALANCE_WITH_SUM,\n    BALANCE_WITH_SUM_AND_GLOBAL,\n    GROUP,\n    GROUP_NO_HEADER,\n    PERCENTAGE,\n    QUANTITY,\n    SHORT_AMOUNT,\n    SHORT_DATE,\n    STRING,\n    TIMESTAMP\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/table/GroupInfo.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.table;\n\nimport java.math.BigDecimal;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport jgnash.util.NotNull;\n\nimport static jgnash.report.table.AbstractReportTableModel.DEFAULT_GROUP;\n\n/**\n * Used to create Groups within a report\n *\n * @author Craig Cavanaugh\n */\npublic class GroupInfo implements Comparable<GroupInfo> {\n\n    /**\n     * Group name\n     */\n    public final String group;\n\n    /**\n     * Summation values for cross tabulation of columns\n     */\n    private final Map<Integer, BigDecimal> summationMap = new HashMap<>();\n\n    private boolean hasSummation = false;\n\n    private GroupInfo(@NotNull final String group) {\n        Objects.requireNonNull(group);\n\n        this.group = group;\n    }\n\n    public static Set<GroupInfo> getGroups(final AbstractReportTableModel tableModel) {\n        final Map<String, GroupInfo> groupInfoMap = new HashMap<>();\n\n        for (int c = 0; c < tableModel.getColumnCount(); c++) {\n            final ColumnStyle columnStyle = tableModel.getColumnStyle(c);\n\n            if (columnStyle == ColumnStyle.GROUP || columnStyle == ColumnStyle.GROUP_NO_HEADER) {\n                for (int r = 0; r < tableModel.getRowCount(); r++) {\n                   groupInfoMap.computeIfAbsent(tableModel.getValueAt(r, c).toString(), GroupInfo::new);\n                }\n            }\n        }\n\n        // create a default group for tables that do not specify one\n        if (groupInfoMap.isEmpty()) {\n            final GroupInfo groupInfo = new GroupInfo(DEFAULT_GROUP);\n            groupInfoMap.put(DEFAULT_GROUP, groupInfo);\n        }\n\n        // perform summation\n        for (int r = 0; r < tableModel.getRowCount(); r++) {\n            final GroupInfo groupInfo = groupInfoMap.get(tableModel.getGroup(r));\n\n            for (int c = 0; c < tableModel.getColumnCount(); c++) {\n                if (tableModel.getColumnClass(c) == BigDecimal.class) {\n                    switch (tableModel.getColumnStyle(c)) {\n                        case AMOUNT_SUM:\n                        case BALANCE_WITH_SUM:\n                        case BALANCE_WITH_SUM_AND_GLOBAL:\n                            groupInfo.addValue(c, (BigDecimal) tableModel.getValueAt(r, c));\n                        default:\n                            break;\n                    }\n                }\n            }\n        }\n\n        return new TreeSet<>(groupInfoMap.values());\n    }\n\n    @Override\n    public int compareTo(final GroupInfo o) {\n        return group.compareTo(o.group);\n    }\n\n    @Override\n    public boolean equals(final Object o) {\n        if (o instanceof GroupInfo) {\n            return group.equals(((GroupInfo) o).group);\n        }\n        return false;\n    }\n\n    private void addValue(final int column, final BigDecimal value) {\n        if (value != null) {    // protect against a null / filtered value\n            summationMap.put(column, getValue(column).add(value));\n            hasSummation = true;\n        }\n    }\n\n    @NotNull\n    public BigDecimal getValue(final int column) {\n        return summationMap.getOrDefault(column, BigDecimal.ZERO);\n    }\n\n    public boolean hasSummation() {\n        return hasSummation;\n    }\n\n    public int hashCode() {\n        return group.hashCode();\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/table/Row.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.table;\n\nimport jgnash.util.Nullable;\n\n/**\n * Support interface to wrap a row of table data into one object\n *\n * @author Craig Cavanaugh\n */\npublic abstract class Row<T> {\n\n    private final T object;\n\n    protected Row(@Nullable T object) {\n        this.object = object;\n    }\n\n    public T getValue() {\n        return object;\n    }\n\n    /**\n     * Returns the value given a column index\n     *\n     * @param columnIndex column index\n     * @return column value\n     */\n    public abstract Object getValueAt(final int columnIndex);\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/table/SortOrder.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.table;\n\nimport jgnash.resource.util.ResourceUtils;\n\n/**\n * Sort order enumeration\n *\n * @author Craig Cavanaugh\n */\npublic enum SortOrder {\n    BY_NAME(ResourceUtils.getString(\"SortOrder.AccountName\")),\n    BY_BALANCE(ResourceUtils.getString(\"SortOrder.AccountBalanceDesc\"));\n\n    private final transient String description;\n\n    SortOrder(final String description) {\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n}\n"
  },
  {
    "path": "jgnash-report-core/src/main/java/jgnash/report/ui/ReportPrintFactory.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.ui;\n\nimport java.awt.print.PageFormat;\nimport java.awt.print.Paper;\nimport java.util.Arrays;\nimport java.util.Locale;\nimport java.util.prefs.Preferences;\n\nimport jgnash.report.pdf.PageSize;\n\n/**\n * Factory class for handling printing preferences\n * \n * @author Craig Cavanaugh\n */\npublic class ReportPrintFactory {\n    private static final String HEIGHT = \"height\";\n    private static final String WIDTH = \"width\";\n    private static final String ORIENTATION = \"orientation\";\n    private static final String IMAGEABLE_HEIGHT = \"imageableHeight\";\n    private static final String IMAGEABLE_WIDTH = \"imageableWidth\";\n    private static final String IMAGEABLE_X = \"imageableX\";\n    private static final String IMAGEABLE_Y = \"imageableY\";\n\n    private static final int DEFAULT_MARGIN = 43; // ~.60\" margin\n\n    private ReportPrintFactory() {\n    }\n\n    /**\n     * Returns the default paper size for a report\n     * \n     * @return page format\n     */\n    public static PageFormat getDefaultPage() {\n\n        /* A4 is the assumed default */\n        PageSize defaultPageSize = PageSize.A4;\n\n        /* US and Canada use letter size as the default */\n        final Locale[] letterLocales = new Locale[] { Locale.US, Locale.CANADA, Locale.CANADA_FRENCH };\n        if (Arrays.asList(letterLocales).contains(Locale.getDefault())) {\n            defaultPageSize = PageSize.LETTER;\n        }\n\n        /* Create the default paper size with a default margin */\n        final Paper paper = new Paper();\n\n        double width = defaultPageSize.width;\n        double height = defaultPageSize.height;\n\n        paper.setSize(width, height);\n        paper.setImageableArea(DEFAULT_MARGIN, DEFAULT_MARGIN, width - (2 * DEFAULT_MARGIN),\n                height - (2 * DEFAULT_MARGIN));\n\n        final PageFormat format = new PageFormat();\n        format.setPaper(paper);\n\n        return format;\n    }\n\n    /**\n     * Save a {@code PageFormat} to preferences\n     * \n     * @param p the specific report {@code Preferences}\n     * @param format {@code PageFormat} to save\n     */\n    public static void savePageFormat(final Preferences p, final PageFormat format) {\n        p.putInt(ORIENTATION, format.getOrientation());\n\n        final Paper paper = format.getPaper();\n\n        p.putDouble(HEIGHT, paper.getHeight());\n        p.putDouble(WIDTH, paper.getWidth());\n\n        p.putDouble(IMAGEABLE_HEIGHT, paper.getImageableHeight());\n        p.putDouble(IMAGEABLE_WIDTH, paper.getImageableWidth());\n        p.putDouble(IMAGEABLE_X, paper.getImageableX());\n        p.putDouble(IMAGEABLE_Y, paper.getImageableY());\n    }\n\n    /**\n     * Generates a {@code PageFormat} given a {@code Preferences} node\n     * @param p {@code Preferences} node\n     * @return restored {@code PageFormat} or the default if it has not been saved before\n     */\n    public static PageFormat getPageFormat(final Preferences p) {\n        double height = p.getDouble(HEIGHT, 0);\n        double width = p.getDouble(WIDTH, 0);\n        int orientation = p.getInt(ORIENTATION, 0);\n        double imageableHeight = p.getDouble(IMAGEABLE_HEIGHT, 0);\n        double imageableWidth = p.getDouble(IMAGEABLE_WIDTH, 0);\n        double imageableX = p.getDouble(IMAGEABLE_X, 0);\n        double imageableY = p.getDouble(IMAGEABLE_Y, 0);\n\n        if (height == 0 || width == 0 || imageableHeight == 0 || imageableWidth == 0) {\n            return getDefaultPage();\n        }\n\n        final PageFormat pf = new PageFormat();\n\n        pf.setOrientation(orientation);\n\n        Paper paper = pf.getPaper();\n        paper.setSize(width, height);\n        paper.setImageableArea(imageableX, imageableY, imageableWidth, imageableHeight);\n\n        pf.setPaper(paper);\n\n        return pf;\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/build.gradle.kts",
    "content": "import org.gradle.internal.jvm.Jvm\nimport org.gradle.internal.os.OperatingSystem.current\nimport java.util.*\n\ndescription = \"jGnash Resources\"\n\nval moduleName = \"jgnash.resources\"\nval osName = current()!!\nval javaVersion = Jvm.current()!!\nval timeStamp = Date()\n\ntasks.withType<ProcessResources> {\n    filesMatching(\"**/constants.properties\") {\n        expand(hashMapOf(\"version\" to version, \"javaVersion\" to javaVersion,\n                \"timeStamp\" to timeStamp, \"osName\" to osName))\n    }\n\n    filesMatching(\"**/notice.html\") {\n        expand(hashMapOf(\"version\" to version, \"javaVersion\" to javaVersion,\n                \"timeStamp\" to timeStamp, \"osName\" to osName))\n    }\n}\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}\n\n\n\n\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/ClassPathUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\nimport java.net.URL;\nimport java.util.Locale;\n\n/**\n * Classpath utilities\n *\n * @author Craig Cavanaugh\n */\npublic class ClassPathUtils {\n\n    private static final String DEFAULT = \"en\";\n\n//    /** Get a set of file names with the specified extension in the specified package path\n//     *\n//     * @param packageName   package path\n//     * @param fileExtension file extension\n//     * @return Set of file names\n//     */\n//    public static Set<String> getFilesByExtension(final String packageName, final String fileExtension) {\n//\n//        Set<String> fileSet = new HashSet<String>();\n//\n//        // Translate the package name into an absolute path\n//        String name = packageName;\n//        if (!name.startsWith(\"/\")) {\n//            name = \"/\" + name;\n//        }\n//\n//        name = name.replace('.', '/');\n//\n//        // Get a File object for the package\n//        URL url = new ClassPathUtils().getClass().getResource(name);\n//\n//        File directory = new File(url.getFile());\n//\n//        if (directory.isDirectory()) {\n//            for (String file : directory.list()) {\n//                if (file.endsWith(fileExtension)) {\n//                    fileSet.add(file);\n//                }\n//            }\n//        }\n//\n//        return fileSet;\n//    }\n\n    /**\n     * Find the best localized path given a root\n     *\n     * @param rootPath root path to start search\n     * @return the path for reading the resource, or null if the resource could not be found\n     */\n    public static String getLocalizedPath(final String rootPath) {\n\n        final Locale locale = Locale.getDefault();\n\n        // Try the language, country, and variant first\n        String lang = locale.getLanguage() + \"_\" + locale.getCountry() + \"_\" + locale.getVariant();\n\n        URL url = ClassPathUtils.class.getResource(rootPath + \"/\" + lang);\n\n        if (url != null) {\n            return rootPath + \"/\" + lang;\n        }\n\n        // Try the language and country first\n        lang = locale.getLanguage() + \"_\" + locale.getCountry();\n\n        url = ClassPathUtils.class.getResource(rootPath + \"/\" + lang);\n        if (url != null) {\n            return rootPath + \"/\" + lang;\n        }\n\n        // Try just the language now\n        lang = locale.getLanguage();\n\n        url = ClassPathUtils.class.getResource(rootPath + \"/\" + lang);\n        if (url != null) {\n            return rootPath + \"/\" + lang;\n        }\n\n        // Just use the default locale\n        url = ClassPathUtils.class.getResource(rootPath + \"/\" + DEFAULT);\n        if (url != null) {\n            return rootPath + \"/\" + DEFAULT;\n        }\n\n        return null;\n    }\n\n    private ClassPathUtils() {\n        // Utility class\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/HTMLResource.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\nimport java.net.URL;\nimport java.util.Locale;\n\n/**\n * This class is for returning URL's to localized html files\n * only language.\n * \n * @author Craig Cavanaugh\n */\npublic class HTMLResource {\n\n    private static final String ROOT_PATH = \"/jgnash/resource/html/\";\n\n    private static final String DEFAULT = \"/jgnash/resource/html/en/\";\n\n    /**\n     * Contains only static methods\n     */\n    private HTMLResource() {\n    }\n\n    /**\n     * Find a locale specific html file given the file name.\n     * \n     * @param fileName the file name of the html file to look for.\n     * @return a URL for reading the resource, or null if the resource could not be found\n     */\n    public static URL getURL(final String fileName) {\n        String lang = Locale.getDefault().getLanguage();\n\n        URL url = HTMLResource.class.getResource(ROOT_PATH + lang + \"/\" + fileName);\n\n        if (url != null) {\n            return url;\n        }\n\n        // try to get the default\n        return HTMLResource.class.getResource(DEFAULT + fileName);\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/MonthName.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\nimport java.text.DateFormatSymbols;\nimport java.time.Month;\n\n/**\n * Human friendly Decorator for {@code Month}\n *\n * @author Craig Cavanaugh\n */\npublic enum MonthName {\n\n    JANUARY(Month.JANUARY, new DateFormatSymbols().getMonths()[0]),\n    FEBRUARY(Month.FEBRUARY, new DateFormatSymbols().getMonths()[1]),\n    MARCH(Month.MARCH, new DateFormatSymbols().getMonths()[2]),\n    APRIL(Month.APRIL, new DateFormatSymbols().getMonths()[3]),\n    MAY(Month.MAY, new DateFormatSymbols().getMonths()[4]),\n    JUNE(Month.JUNE, new DateFormatSymbols().getMonths()[5]),\n    JULY(Month.JULY, new DateFormatSymbols().getMonths()[6]),\n    AUGUST(Month.AUGUST, new DateFormatSymbols().getMonths()[7]),\n    SEPTEMBER(Month.SEPTEMBER, new DateFormatSymbols().getMonths()[8]),\n    OCTOBER(Month.OCTOBER, new DateFormatSymbols().getMonths()[9]),\n    NOVEMBER(Month.NOVEMBER, new DateFormatSymbols().getMonths()[10]),\n    DECEMBER(Month.DECEMBER, new DateFormatSymbols().getMonths()[11]);\n\n    private final Month month;\n    private final String description;\n\n    MonthName(final Month month, final String description) {\n        this.month = month;\n        this.description = description;\n    }\n\n    @Override\n    public String toString() {\n        return description;\n    }\n\n    public static MonthName valueOf(final Month month) {\n        return values()[month.ordinal()];\n    }\n\n    public Month getMonth() {\n        return month;\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/OS.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\n/**\n * OS specific detection code.\n * \n * @author Craig Cavanaugh\n */\npublic final class OS {\n\n    private static final boolean IS_OSX;\n\n    private static final boolean IS_WINDOWS;\n\n    public static final String JAVA_VERSION = \"java.version\";\n\n    private static final String JAVA_SPEC_VERSION = \"java.specification.version\";\n\n    static {\n        final String os = System.getProperty(\"os.name\");\n\n        IS_OSX = os.startsWith(\"Darwin\") || os.startsWith(\"Mac\");\n        IS_WINDOWS = os.startsWith(\"Windows\");\n    }\n\n    private OS() {\n        // utility class\n    }\n\n    /**\n     * Determines if running on OSX.\n     * \n     * @return true if running on OSX\n     */\n    @SuppressWarnings(\"unused\")\n    public static boolean isSystemOSX() {\n        return IS_OSX;\n    }\n\n    /**\n     * Determines if running on Windows.\n     * \n     * @return true if running on Windows\n     */\n    public static boolean isSystemWindows() {\n        return IS_WINDOWS;\n    }\n\n    /**\n     * Returns the version of the JVM.\n     *\n     * @return returns 1.8 given 1.8.0_60\n     */\n    public static float getJavaVersion() {\n        return Float.parseFloat(System.getProperty(JAVA_SPEC_VERSION));\n    }\n\n    /**\n     * Returns the release of the JVM.\n     *\n     * @return returns 60 given 1.8.0_60-ea\n     */\n    public static int getJavaRelease() {\n        String release = System.getProperty(JAVA_VERSION).substring(6);\n\n        release = release.replaceAll(\"[^\\\\d.]\", \"\").trim();\n\n        return Integer.parseInt(release);\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/ResourceUtils.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\nimport java.text.MessageFormat;\nimport java.util.Locale;\nimport java.util.MissingResourceException;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.prefs.Preferences;\nimport java.util.regex.Pattern;\n\n/**\n * @author Craig Cavanaugh\n */\npublic class ResourceUtils {\n\n    /**\n     * key for locale preference\n     */\n    private static final String LOCALE = \"locale\";\n\n    private static final String DEFAULT_RESOURCE_BUNDLE = \"jgnash/resource/resource\";\n\n    /**\n     * Historical path to the preference root\n     */\n    private static final String PREFERENCE_NODE = \"/jgnash/util/Resource\";\n\n    private static ResourceBundle resourceBundle;\n\n    static {\n        final Preferences p = Preferences.userRoot().node(PREFERENCE_NODE);\n        Locale.setDefault(decodeLocale(p.get(LOCALE, \"\")));\n    }\n\n    private ResourceUtils() {\n        // Utility class\n    }\n\n    public static synchronized ResourceBundle getBundle() {\n        if (resourceBundle == null) {\n\n            try {\n                resourceBundle = ResourceBundle.getBundle(DEFAULT_RESOURCE_BUNDLE);\n            } catch (final MissingResourceException e) {\n                Logger.getLogger(ResourceUtils.class.getName()).log(Level.WARNING, \"Could not find correct resource bundle\", e);\n                resourceBundle = ResourceBundle.getBundle(DEFAULT_RESOURCE_BUNDLE, Locale.ENGLISH);\n            }\n        }\n\n        return resourceBundle;\n    }\n\n    /**\n     * Gets a localized string with arguments\n     *\n     * @param key The key for the localized string\n     * @param arguments arguments to pass the the message formatter\n     * @return The localized string\n     */\n    public static String getString(final String key, final Object... arguments) {\n        try {\n            if (arguments.length == 0) {\n                return getBundle().getString(key);\n            }\n\t\t\treturn MessageFormat.format(getBundle().getString(key), arguments);\n        } catch (final MissingResourceException mre) {\n            Logger.getLogger(ResourceUtils.class.getName()).log(Level.WARNING, \"Missing resource for: \" + key, mre);\n            return key;\n        }\n    }\n\n    /**\n     * Sets the new default locale.  This must be called if overridden.\n     *\n     * @param l The new default locale\n     */\n    public static void setLocale(final Locale l) {\n        Locale.setDefault(l);\n        final Preferences p = Preferences.userRoot().node(PREFERENCE_NODE);\n        p.put(LOCALE, encodeLocale(l));\n        resourceBundle = null;  // force a reload the resource bundle\n    }\n\n    private static String encodeLocale(final Locale locale) {\n        final StringBuilder buf = new StringBuilder();\n\n        buf.append(locale.getLanguage());\n        if (!locale.getCountry().isEmpty()) {\n            buf.append('.');\n            buf.append(locale.getCountry());\n            if (!locale.getVariant().isEmpty()) {\n                buf.append('.');\n                buf.append(locale.getVariant());\n            }\n        }\n        return buf.toString();\n    }\n\n    private static Locale decodeLocale(final String locale) {\n        if (locale == null || locale.isEmpty() || locale.equals(\"null\")) {\n            return Locale.getDefault();\n        } else if (locale.indexOf('.') == -1) {\n            return new Locale(locale);\n        } else {\n            final Pattern pattern = Pattern.compile(\"\\\\x2E\");\n\n            final String[] array = pattern.split(locale);\n\n            switch (array.length) {\n                case 3:\n                    return new Locale(array[0], array[1], array[2]);\n                case 2:\n                    return new Locale(array[0], array[1]);\n                default:  // should not happen\n                    return Locale.getDefault();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/TextResource.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.StandardCharsets;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * This class is for returning URL's to localized text files.\n *\n * @author Craig Cavanaugh\n */\npublic class TextResource {\n\n    private static final String ROOT_PATH = \"/jgnash/resource/text\";\n\n    /**\n     * Contains only static methods\n     */\n    private TextResource() {\n    }\n\n    /**\n     * Find a locale specific text file given the file name. Multiple lines of text are preserved.\n     *\n     * @param fileName the file name of the text file to look for.\n     * @return a String containing the contents of the file.\n     */\n    public static String getString(final String fileName) {\n\n        final String root = ClassPathUtils.getLocalizedPath(ROOT_PATH);\n        final StringBuilder sb = new StringBuilder();\n\n        try (final InputStream s = TextResource.class.getResourceAsStream(root + \"/\" + fileName)) {\n            if (s != null) {\n                try (final BufferedReader b = new BufferedReader(new InputStreamReader(s, StandardCharsets.UTF_8))) {\n                    String t = loadConvert(b.readLine());\n                    while (t != null) {\n                        sb.append(t);\n                        t = loadConvert(b.readLine());\n                        if (t != null) {\n                            sb.append('\\n');\n                        }\n                    }\n                }\n            }\n        } catch (final IOException e) {\n            Logger.getLogger(TextResource.class.getName()).log(Level.SEVERE, null, e);\n        }\n\n        return sb.toString();\n    }\n\n    /**\n     * Converts encoded &#92;uxxxx to unicode chars and changes special saved chars to their original forms\n     * <p>\n     * This was copied from the JDK source code; Properties.java\n     *\n     * @param string unicode formatted string\n     * @return string without unicode characters\n     */\n    private static String loadConvert(final String string) {\n\n        if (string == null) {\n            return null;\n        }\n\n        char aChar;\n\n        final int len = string.length();\n\n        final StringBuilder outBuffer = new StringBuilder(len);\n\n        for (int x = 0; x < len; ) {\n            aChar = string.charAt(x++);\n            if (aChar == '\\\\') {\n                aChar = string.charAt(x++);\n                if (aChar == 'u') {\n                    // Read the xxxx\n                    int value = 0;\n                    for (int i = 0; i < 4; i++) {\n                        aChar = string.charAt(x++);\n                        switch (aChar) {\n                            case '0':\n                            case '1':\n                            case '2':\n                            case '3':\n                            case '4':\n                            case '5':\n                            case '6':\n                            case '7':\n                            case '8':\n                            case '9':\n                                value = (value << 4) + aChar - '0';\n                                break;\n                            case 'a':\n                            case 'b':\n                            case 'c':\n                            case 'd':\n                            case 'e':\n                            case 'f':\n                                value = (value << 4) + 10 + aChar - 'a';\n                                break;\n                            case 'A':\n                            case 'B':\n                            case 'C':\n                            case 'D':\n                            case 'E':\n                            case 'F':\n                                value = (value << 4) + 10 + aChar - 'A';\n                                break;\n                            default:\n                                throw new IllegalArgumentException(\"Malformed \\\\uxxxx encoding.\");\n                        }\n                    }\n                    outBuffer.append((char) value);\n                } else {\n                    switch (aChar) {\n                        case 't':\n                            aChar = '\\t';\n                            break;\n                        case 'r':\n                            aChar = '\\r';\n                            break;\n                        case 'n':\n                            aChar = '\\n';\n                            break;\n                        case 'f':\n                            aChar = '\\f';\n                            break;\n                    }\n                    outBuffer.append(aChar);\n                }\n            } else {\n                outBuffer.append(aChar);\n            }\n        }\n        return outBuffer.toString();\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/java/jgnash/resource/util/Version.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.resource.util;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Optional;\nimport java.util.ResourceBundle;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Utility class for application version.\n *\n * @author Craig Cavanaugh\n */\npublic class Version {\n\n    private static final String JGNASH_RESOURCE_CONSTANTS = \"jgnash/resource/constants\";\n\n    private static final String VERSION = \"version\";\n\n    private static final String NAME = \"name\";\n\n    private static final String JSON_NAME = \"name\";\n\n    private static final String TAG_URL = \"https://api.github.com/repos/ccavanaugh/jgnash/tags\";\n\n    private static final String REGEX = \"\\\"(.+?)\\\"\";\n\n    private static final int CONNECT_TIMEOUT = 5000;\n\n    private Version() {\n        // Utility class\n    }\n\n    public static String getAppVersion() {\n        return ResourceBundle.getBundle(JGNASH_RESOURCE_CONSTANTS).getString(VERSION);\n    }\n\n    public static String getAppName() {\n        return ResourceBundle.getBundle(JGNASH_RESOURCE_CONSTANTS).getString(NAME);\n    }\n\n    public static Optional<String> getLatestGitHubRelease() {\n\n        try {\n            final StringBuilder builder = new StringBuilder();\n\n            final URL url = new URL(TAG_URL);\n            final URLConnection connection = url.openConnection();\n            connection.setConnectTimeout(CONNECT_TIMEOUT);\n\n            try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),\n                    StandardCharsets.UTF_8))) {\n                for (String line; (line = reader.readLine()) != null; ) {\n                    builder.append(line);\n                }\n            }\n\n            final Pattern pattern = Pattern.compile(REGEX);\n            final Matcher matcher = pattern.matcher(builder.toString());\n\n            while (matcher.find()) {\n                if (matcher.group(1).equals(JSON_NAME) && matcher.find()) {\n                    return Optional.ofNullable(matcher.group(1));\n                }\n            }\n\n            return Optional.empty();\n        } catch (IOException ioe) {\n            if (ioe.getLocalizedMessage().contains(\"403 for URL\")) {  // known github error, return the current version\n                return Optional.of(getAppVersion());\n            }\n            return Optional.empty();\n        } catch (final Exception e) {\n            Logger.getLogger(Version.class.getName()).log(Level.WARNING, e.getLocalizedMessage(), e);\n            return Optional.empty();\n        }\n    }\n\n    public static boolean isReleaseCurrent() {\n        return isReleaseCurrent(getAppVersion());\n    }\n\n    public static boolean isReleaseCurrent(final String version) {\n        boolean currentRelease = true;\n\n        final Optional<String> release = getLatestGitHubRelease();\n\n        if (release.isPresent()) {\n            // quick check\n            if (version.equals(release.get())) {\n                return true;\n            } else if (version.startsWith(\"${\")) {   // running withing a development environment\n                return true;\n            } else {\n                final String[] gitVersion = release.get().split(\"\\\\.\");\n                final String[] thisVersion = version.split(\"\\\\.\");\n\n                for (int i = 0; i < 3; i++) {   // x.x.x is tested for\n                    if (i < gitVersion.length && i < thisVersion.length) {\n                        try {\n                            final int current = Integer.parseInt(thisVersion[i]);\n                            final int git = Integer.parseInt(gitVersion[i]);\n\n                            if (git > current) {\n                                currentRelease = false;\n                                break;\n                            } else if (current > git) {    // newer release than git\n                                break;\n                            }\n                        } catch (final NumberFormatException e) {\n                            Logger.getLogger(Version.class.getName()).log(Level.INFO, e.getLocalizedMessage(), e);\n                        }\n                    }\n                }\n\n                if (gitVersion.length > thisVersion.length && currentRelease) {\n                    currentRelease = false;\n                }\n            }\n        }\n        return currentRelease;\n    }\n}\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/cs/common.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Společné položky\" description=\"Common Accounts\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"en_US\" symbol=\"USD\" scale=\"2\" prefix=\"$\" suffix=\"\" description=\"en_US\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Bankovní položky\" description=\"Bank Accounts\">\n        <accountType>BANK</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Hotovost\" description=\"Cash Account\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\"/>\n            <securities id=\"10\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"11\"/>\n          </Account>\n          <Account id=\"12\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Spoření\" description=\"Savings Account\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"13\"/>\n            <securities id=\"14\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"15\"/>\n          </Account>\n        </children>\n        <securities id=\"16\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"17\"/>\n      </Account>\n      <Account id=\"18\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Výdajové položky\" description=\"Expense Accounts\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"20\">\n          <Account id=\"21\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Auto\" description=\"Description\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"22\">\n              <Account id=\"23\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Pohonné hmoty\" description=\"Fuel Expenses\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"24\"/>\n                <securities id=\"25\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"26\"/>\n              </Account>\n              <Account id=\"27\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Pojištění\" description=\"Auto Insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"28\"/>\n                <securities id=\"29\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"30\"/>\n              </Account>\n              <Account id=\"31\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Servis\" description=\"Auto Services\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes>Oil, tires, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"32\"/>\n                <securities id=\"33\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"34\"/>\n              </Account>\n            </children>\n            <securities id=\"35\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"36\"/>\n          </Account>\n          <Account id=\"37\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Účty\" description=\"Bills\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"38\">\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Mobilní telefon\" description=\"Cell Phone\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Telefon\" description=\"Telephone\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n            </children>\n            <securities id=\"47\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"48\"/>\n          </Account>\n          <Account id=\"49\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Jídlo\" description=\"Food\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"50\">\n              <Account id=\"51\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Jídla mimo dům\" description=\"Dinning Out\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"52\"/>\n                <securities id=\"53\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"54\"/>\n              </Account>\n              <Account id=\"55\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Potraviny\" description=\"Groceries\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"56\"/>\n                <securities id=\"57\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"58\"/>\n              </Account>\n            </children>\n            <securities id=\"59\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"60\"/>\n          </Account>\n          <Account id=\"61\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Zdraví\" description=\"Health\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"62\">\n              <Account id=\"63\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Pojištění\" description=\"Medical insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"64\"/>\n                <securities id=\"65\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"66\"/>\n              </Account>\n              <Account id=\"67\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Zdravotní péče\" description=\"Major Medical Expense\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"68\"/>\n                <securities id=\"69\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"70\"/>\n              </Account>\n              <Account id=\"71\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Léky\" description=\"Medication\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"72\"/>\n                <securities id=\"73\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"74\"/>\n              </Account>\n              <Account id=\"75\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Práce\" description=\"Office Visits\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"76\"/>\n                <securities id=\"77\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"78\"/>\n              </Account>\n              <Account id=\"79\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Rekreace (lázně, sauna)\" description=\"Wellness\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes>Hait cuts, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"80\"/>\n                <securities id=\"81\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"82\"/>\n              </Account>\n            </children>\n            <securities id=\"83\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"84\"/>\n          </Account>\n          <Account id=\"85\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Domácnost\" description=\"Houseold\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"86\">\n              <Account id=\"87\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Spotřebiče/Nábytek\" description=\"Appliances and Furniture\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"88\"/>\n                <securities id=\"89\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"90\"/>\n              </Account>\n              <Account id=\"91\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Oblečení/Ložní prádlo\" description=\"Clothing and Bedding\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"92\"/>\n                <securities id=\"93\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"94\"/>\n              </Account>\n              <Account id=\"95\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Elektronika\" description=\"Electronics for the house\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"96\"/>\n                <securities id=\"97\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"98\"/>\n              </Account>\n              <Account id=\"99\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Různé spotřební zboží\" description=\"Misc Consumables\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes>Stamps, batteries, etc</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"100\"/>\n                <securities id=\"101\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"102\"/>\n              </Account>\n              <Account id=\"103\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Toaletní potřeby\" description=\"Toiletries\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"104\"/>\n                <securities id=\"105\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"106\"/>\n              </Account>\n            </children>\n            <securities id=\"107\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"108\"/>\n          </Account>\n          <Account id=\"109\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Různé\" description=\"Miscellaneous\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"110\">\n              <Account id=\"111\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Bankovní poplatky\" description=\"Bank Fees\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes>ATM Fees, OD protection, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"112\"/>\n                <securities id=\"113\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"114\"/>\n              </Account>\n              <Account id=\"115\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Příspěvky na charitu\" description=\"Charitable Contributions\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"116\"/>\n                <securities id=\"117\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"118\"/>\n              </Account>\n              <Account id=\"119\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Životní pojištění\" description=\"Life Insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"120\"/>\n                <securities id=\"121\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"122\"/>\n              </Account>\n            </children>\n            <securities id=\"123\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"124\"/>\n          </Account>\n          <Account id=\"125\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Rekreace\" description=\"\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"126\">\n              <Account id=\"127\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Knihy/Časopisy\" description=\"Books and Magazines\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes>Books and magzines</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"128\"/>\n                <securities id=\"129\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"130\"/>\n              </Account>\n              <Account id=\"131\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Koncerty/Filmy\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"132\"/>\n                <securities id=\"133\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"134\"/>\n              </Account>\n              <Account id=\"135\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Záliby\" description=\"Misc Hobbies\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"136\"/>\n                <securities id=\"137\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"138\"/>\n              </Account>\n              <Account id=\"139\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Ubytování\" description=\"Hotels / Etc.\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"140\"/>\n                <securities id=\"141\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"142\"/>\n              </Account>\n              <Account id=\"143\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Různé\" description=\"Misc\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"144\"/>\n                <securities id=\"145\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"146\"/>\n              </Account>\n              <Account id=\"147\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Předplatné\" description=\"Magazine Subscriptions\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"148\"/>\n                <securities id=\"149\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"150\"/>\n              </Account>\n            </children>\n            <securities id=\"151\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"152\"/>\n          </Account>\n          <Account id=\"153\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Daně\" description=\"Description\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"154\">\n              <Account id=\"155\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Okresní daně\" description=\"County tax\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"156\"/>\n                <securities id=\"157\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"158\"/>\n              </Account>\n              <Account id=\"159\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Celostátní daně\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"160\"/>\n                <securities id=\"161\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"162\"/>\n              </Account>\n              <Account id=\"163\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Lékařská péče\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"164\"/>\n                <securities id=\"165\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"166\"/>\n              </Account>\n              <Account id=\"167\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Sociální zabezpečení\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"168\"/>\n                <securities id=\"169\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"170\"/>\n              </Account>\n              <Account id=\"171\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Státní daně\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"172\"/>\n                <securities id=\"173\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"174\"/>\n              </Account>\n            </children>\n            <securities id=\"175\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"176\"/>\n          </Account>\n        </children>\n        <securities id=\"177\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"178\"/>\n      </Account>\n      <Account id=\"179\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Příjmové položky\" description=\"Income Accounts\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"181\">\n          <Account id=\"182\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Příjem - nezdanitelný\" description=\"Non taxable income\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"183\"/>\n            <securities id=\"184\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"185\"/>\n          </Account>\n          <Account id=\"186\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Úroky\" description=\"Interest Income\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"187\"/>\n            <securities id=\"188\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"189\"/>\n          </Account>\n          <Account id=\"190\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Jiné příjmy\" description=\"Other\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"191\"/>\n            <securities id=\"192\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"193\"/>\n          </Account>\n          <Account id=\"194\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Plat\" description=\"Salary\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"195\"/>\n            <securities id=\"196\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"197\"/>\n          </Account>\n        </children>\n        <securities id=\"198\"/>\n        <accountNumber>95</accountNumber>\n        <propertyMap id=\"199\"/>\n      </Account>\n      <Account id=\"200\" placeHolder=\"false\" locked=\"false\" visible=\"false\" name=\"Zahajovací bilance\" description=\"Opening Balances\">\n        <accountType>EQUITY</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"202\"/>\n        <securities id=\"203\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"204\"/>\n      </Account>\n    </children>\n    <securities id=\"205\"/>\n    <propertyMap id=\"206\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/cs/set.txt",
    "content": "common.xml\nspouse.xml\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/cs/spouse.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Příjmy partnera/partnerky\" description=\"Spouse&apos;s Income\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"en_US\" symbol=\"USD\" scale=\"2\" prefix=\"$\" suffix=\"\" description=\"en_US\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Výdajové položky\" description=\"Expense Accounts\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Daně partnera/partnerky\" description=\"Spouse&apos;s Taxes\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\">\n              <Account id=\"10\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Okresní daně\" description=\"County tax\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"11\"/>\n                <securities id=\"12\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"13\"/>\n              </Account>\n              <Account id=\"14\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Lékařská péče\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"15\"/>\n                <securities id=\"16\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"17\"/>\n              </Account>\n              <Account id=\"18\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Sociální zabezpečení\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"19\"/>\n                <securities id=\"20\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"21\"/>\n              </Account>\n              <Account id=\"22\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Státní daně\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"23\"/>\n                <securities id=\"24\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"25\"/>\n              </Account>\n            </children>\n            <securities id=\"26\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"27\"/>\n          </Account>\n        </children>\n        <securities id=\"28\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"29\"/>\n      </Account>\n      <Account id=\"30\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Příjmové položky\" description=\"Income Accounts\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"32\">\n          <Account id=\"33\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Příjem partnera/partnerky\" description=\"Spouse&apos;s Income Accounts\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"30\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"34\">\n              <Account id=\"35\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Příjem - nezdanitelný\" description=\"Non taxable income\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"36\"/>\n                <securities id=\"37\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"38\"/>\n              </Account>\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Úroky\" description=\"Interest Income\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Jiné příjmy\" description=\"Other\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n              <Account id=\"47\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Plat\" description=\"Salary\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"48\"/>\n                <securities id=\"49\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"50\"/>\n              </Account>\n            </children>\n            <securities id=\"51\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"52\"/>\n          </Account>\n        </children>\n        <securities id=\"53\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"54\"/>\n      </Account>\n    </children>\n    <securities id=\"55\"/>\n    <propertyMap id=\"56\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/de/set.txt",
    "content": "standard.xml\r\nspouse.xml\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/de/spouse.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Partner\" description=\"Partner\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"de_DE\" symbol=\"EUR\" scale=\"2\" prefix=\"\" suffix=\"€\" description=\"de_DE\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Ausgaben\" description=\"Ausgaben\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <securities id=\"28\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"29\"/>\n      </Account>\n      <Account id=\"30\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Einnahmen\" description=\"Einnahmen\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"32\">\n          <Account id=\"33\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Einkommen Partner\" description=\"Einkommen Partner\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"30\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"34\">\n              <Account id=\"35\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Gehalt\" description=\"Gehalt\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"36\"/>\n                <securities id=\"37\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"38\"/>\n              </Account>\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Zinsen\" description=\"Zinsen\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Anderes Einkommen\" description=\"Anderes Einkommen\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n            </children>\n            <securities id=\"51\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"52\"/>\n          </Account>\n        </children>\n        <securities id=\"53\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"54\"/>\n      </Account>\n    </children>\n    <securities id=\"55\"/>\n    <propertyMap id=\"56\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/de/standard.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Standard Konten\" description=\"Standard Konten\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"de_DE\" symbol=\"EUR\" scale=\"2\" prefix=\"\" suffix=\"€\" description=\"de_DE\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Bankkonten\" description=\"Bankkonten\">\n        <accountType>BANK</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Giro\" description=\"Girokonto\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\"/>\n            <securities id=\"10\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"11\"/>\n          </Account>\n          <Account id=\"12\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Sparen\" description=\"Sparkonto\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"13\"/>\n            <securities id=\"14\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"15\"/>\n          </Account>\n        </children>\n        <securities id=\"16\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"17\"/>\n      </Account>\n      <Account id=\"18\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Ausgaben\" description=\"Ausgaben\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"20\">\n          <Account id=\"21\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Auto\" description=\"Ausgaben Auto\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"22\">\n              <Account id=\"23\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Treibstoff\" description=\"Treibstoff\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"24\"/>\n                <securities id=\"25\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"26\"/>\n              </Account>\n              <Account id=\"27\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Versicherung\" description=\"KFZ Versicherung\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"28\"/>\n                <securities id=\"29\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"30\"/>\n              </Account>\n              <Account id=\"31\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Werkstatt\" description=\"Autowerkstatt\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes>Oil, tires, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"32\"/>\n                <securities id=\"33\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"34\"/>\n              </Account>\n            </children>\n            <securities id=\"35\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"36\"/>\n          </Account>\n          <Account id=\"37\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Rechnungen\" description=\"Rechnungen\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"38\">\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Handy\" description=\"Handy\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Festnetz/Internet\" description=\"Festnetz/Internet\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n            </children>\n            <securities id=\"47\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"48\"/>\n          </Account>\n          <Account id=\"49\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Essen\" description=\"Essen\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"50\">              \n              <Account id=\"51\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Restaurant\" description=\"Restaurant\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"52\"/>\n                <securities id=\"53\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"54\"/>\n              </Account>\n              <Account id=\"55\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Einkaufen\" description=\"Einkaufen\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"56\"/>\n                <securities id=\"57\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"58\"/>\n              </Account>\n            </children>\n            <securities id=\"59\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"60\"/>\n          </Account>\n          <Account id=\"61\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Gesundheit\" description=\"Gesundheit\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"62\">\n              <Account id=\"71\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Medikamente\" description=\"Medikamente\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"72\"/>\n                <securities id=\"73\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"74\"/>\n              </Account>\n              <Account id=\"79\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Wellness\" description=\"Wellness\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes>Hait cuts, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"80\"/>\n                <securities id=\"81\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"82\"/>\n              </Account>\n            </children>\n            <securities id=\"83\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"84\"/>\n          </Account>\n          <Account id=\"85\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Haushalt\" description=\"Haushalt\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"86\">\n              <Account id=\"87\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Möbel\" description=\"Möbel\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"88\"/>\n                <securities id=\"89\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"90\"/>\n              </Account>\n              <Account id=\"91\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Kleidung\" description=\"Kleidung\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"92\"/>\n                <securities id=\"93\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"94\"/>\n              </Account>\n              <Account id=\"95\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Geräte\" description=\"Geräte\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"96\"/>\n                <securities id=\"97\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"98\"/>\n              </Account>\n              <Account id=\"99\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Versch. Verbrauchsgegenstände\" description=\"Verschiedene Verbrauchsgegenstände\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes>Stamps, batteries, etc</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"100\"/>\n                <securities id=\"101\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"102\"/>\n              </Account>\n              <Account id=\"103\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Hygieneartikel\" description=\"Hygieneartikel\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"104\"/>\n                <securities id=\"105\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"106\"/>\n              </Account>\n            </children>\n            <securities id=\"107\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"108\"/>\n          </Account>\n          <Account id=\"109\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Verschiedenes\" description=\"Verschiedenes\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"110\">\n              <Account id=\"111\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Bankgebühren\" description=\"Bankgebühren\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes>ATM Fees, OD protection, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"112\"/>\n                <securities id=\"113\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"114\"/>\n              </Account>\n              <Account id=\"115\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Spenden\" description=\"Spenden\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"116\"/>\n                <securities id=\"117\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"118\"/>\n              </Account>\n              <Account id=\"119\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Lebensversicherung\" description=\"Lebensversicherung\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"120\"/>\n                <securities id=\"121\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"122\"/>\n              </Account>\n            </children>\n            <securities id=\"123\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"124\"/>\n          </Account>\n          <Account id=\"125\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Freizeit\" description=\"Freizeit\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"126\">\n              <Account id=\"127\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Bücher / Zeitschriften\" description=\"Bücher und Zeitschriften\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes>Books and magzines</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"128\"/>\n                <securities id=\"129\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"130\"/>\n              </Account>\n              <Account id=\"131\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Konzerte / Kino\" description=\"Konzerte und Kino\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"132\"/>\n                <securities id=\"133\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"134\"/>\n              </Account>\n              <Account id=\"135\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Hobby\" description=\"Hobby\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"136\"/>\n                <securities id=\"137\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"138\"/>\n              </Account>\n              <Account id=\"139\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Unterkunft\" description=\"Hotels / Etc.\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"140\"/>\n                <securities id=\"141\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"142\"/>\n              </Account>\n              <Account id=\"143\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Verschiedenes\" description=\"Verschiedenes\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"144\"/>\n                <securities id=\"145\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"146\"/>\n              </Account>\n              <Account id=\"147\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Abos\" description=\"Abos\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"148\"/>\n                <securities id=\"149\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"150\"/>\n              </Account>\n            </children>\n            <securities id=\"151\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"152\"/>\n          </Account>\n        </children>\n        <securities id=\"177\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"178\"/>\n      </Account>\n      <Account id=\"179\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Einkommen\" description=\"Einkommen\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"181\">\n          <Account id=\"182\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Gehalt\" description=\"Gehalt\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"183\"/>\n            <securities id=\"184\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"185\"/>\n          </Account>\n          <Account id=\"186\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Zinsen\" description=\"Zinsen\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"187\"/>\n            <securities id=\"188\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"189\"/>\n          </Account>\n          <Account id=\"190\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Anders Einkommen\" description=\"Anderes Einkommen\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"191\"/>\n            <securities id=\"192\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"193\"/>\n          </Account>\n        </children>\n        <securities id=\"198\"/>\n        <accountNumber>95</accountNumber>\n        <propertyMap id=\"199\"/>\n      </Account>\n      <Account id=\"200\" placeHolder=\"false\" locked=\"false\" visible=\"false\" name=\"Startguthaben\" description=\"Startguthaben\">\n        <accountType>EQUITY</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"202\"/>\n        <securities id=\"203\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"204\"/>\n      </Account>\n    </children>\n    <securities id=\"205\"/>\n    <propertyMap id=\"206\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/en/common.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Common Accounts\" description=\"Common Accounts\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"en_US\" symbol=\"USD\" scale=\"2\" prefix=\"$\" suffix=\"\" description=\"en_US\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Bank Accounts\" description=\"Bank Accounts\">\n        <accountType>BANK</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Cash\" description=\"Cash Account\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\"/>\n            <securities id=\"10\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"11\"/>\n          </Account>\n          <Account id=\"12\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Savings\" description=\"Savings Account\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"13\"/>\n            <securities id=\"14\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"15\"/>\n          </Account>\n        </children>\n        <securities id=\"16\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"17\"/>\n      </Account>\n      <Account id=\"18\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Expense Accounts\" description=\"Expense Accounts\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"20\">\n          <Account id=\"21\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Automobile\" description=\"Description\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"22\">\n              <Account id=\"23\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Fuel\" description=\"Fuel Expenses\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"24\"/>\n                <securities id=\"25\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"26\"/>\n              </Account>\n              <Account id=\"27\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Insurance\" description=\"Auto Insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"28\"/>\n                <securities id=\"29\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"30\"/>\n              </Account>\n              <Account id=\"31\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Service\" description=\"Auto Services\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes>Oil, tires, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"32\"/>\n                <securities id=\"33\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"34\"/>\n              </Account>\n            </children>\n            <securities id=\"35\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"36\"/>\n          </Account>\n          <Account id=\"37\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Bills\" description=\"Bills\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"38\">\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Cell Phone\" description=\"Cell Phone\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Telephone\" description=\"Telephone\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n            </children>\n            <securities id=\"47\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"48\"/>\n          </Account>\n          <Account id=\"49\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Food\" description=\"Food\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"50\">              \n              <Account id=\"51\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Dining\" description=\"Dining Out\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"52\"/>\n                <securities id=\"53\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"54\"/>\n              </Account>\n              <Account id=\"55\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Groceries\" description=\"Groceries\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"56\"/>\n                <securities id=\"57\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"58\"/>\n              </Account>\n            </children>\n            <securities id=\"59\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"60\"/>\n          </Account>\n          <Account id=\"61\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Health\" description=\"Health\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"62\">\n              <Account id=\"63\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Insurance\" description=\"Medical insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"64\"/>\n                <securities id=\"65\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"66\"/>\n              </Account>\n              <Account id=\"67\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Major Medical\" description=\"Major Medical Expense\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"68\"/>\n                <securities id=\"69\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"70\"/>\n              </Account>\n              <Account id=\"71\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Medication\" description=\"Medication\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"72\"/>\n                <securities id=\"73\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"74\"/>\n              </Account>\n              <Account id=\"75\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Office\" description=\"Office Visits\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"76\"/>\n                <securities id=\"77\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"78\"/>\n              </Account>\n              <Account id=\"79\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Wellness\" description=\"Wellness\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes>Hait cuts, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"80\"/>\n                <securities id=\"81\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"82\"/>\n              </Account>\n            </children>\n            <securities id=\"83\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"84\"/>\n          </Account>\n          <Account id=\"85\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Household\" description=\"Houseold\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"86\">\n              <Account id=\"87\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Appliances/Furniture\" description=\"Appliances and Furniture\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"88\"/>\n                <securities id=\"89\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"90\"/>\n              </Account>\n              <Account id=\"91\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Clothing/Bedding\" description=\"Clothing and Bedding\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"92\"/>\n                <securities id=\"93\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"94\"/>\n              </Account>\n              <Account id=\"95\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Electronics\" description=\"Electronics for the house\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"96\"/>\n                <securities id=\"97\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"98\"/>\n              </Account>\n              <Account id=\"99\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Misc Consumables\" description=\"Misc Consumables\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes>Stamps, batteries, etc</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"100\"/>\n                <securities id=\"101\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"102\"/>\n              </Account>\n              <Account id=\"103\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Toiletries\" description=\"Toiletries\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"104\"/>\n                <securities id=\"105\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"106\"/>\n              </Account>\n            </children>\n            <securities id=\"107\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"108\"/>\n          </Account>\n          <Account id=\"109\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Miscellaneous\" description=\"Miscellaneous\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"110\">\n              <Account id=\"111\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Bank Fees\" description=\"Bank Fees\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes>ATM Fees, OD protection, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"112\"/>\n                <securities id=\"113\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"114\"/>\n              </Account>\n              <Account id=\"115\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Charitable Contribution\" description=\"Charitable Contributions\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"116\"/>\n                <securities id=\"117\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"118\"/>\n              </Account>\n              <Account id=\"119\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Life Insurance\" description=\"Life Insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"120\"/>\n                <securities id=\"121\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"122\"/>\n              </Account>\n            </children>\n            <securities id=\"123\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"124\"/>\n          </Account>\n          <Account id=\"125\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Recreation\" description=\"\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"126\">\n              <Account id=\"127\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Books / Magazines\" description=\"Books and Magazines\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes>Books and magzines</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"128\"/>\n                <securities id=\"129\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"130\"/>\n              </Account>\n              <Account id=\"131\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Concerts / Movies\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"132\"/>\n                <securities id=\"133\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"134\"/>\n              </Account>\n              <Account id=\"135\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Hobbies\" description=\"Misc Hobbies\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"136\"/>\n                <securities id=\"137\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"138\"/>\n              </Account>\n              <Account id=\"139\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Lodging\" description=\"Hotels / Etc.\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"140\"/>\n                <securities id=\"141\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"142\"/>\n              </Account>\n              <Account id=\"143\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Misc\" description=\"Misc\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"144\"/>\n                <securities id=\"145\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"146\"/>\n              </Account>\n              <Account id=\"147\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Subscriptions\" description=\"Magazine Subscriptions\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"148\"/>\n                <securities id=\"149\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"150\"/>\n              </Account>\n            </children>\n            <securities id=\"151\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"152\"/>\n          </Account>\n          <Account id=\"153\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Taxes\" description=\"Description\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"154\">\n              <Account id=\"155\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"County\" description=\"County tax\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"156\"/>\n                <securities id=\"157\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"158\"/>\n              </Account>\n              <Account id=\"159\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Federal\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"160\"/>\n                <securities id=\"161\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"162\"/>\n              </Account>\n              <Account id=\"163\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Medicare\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"164\"/>\n                <securities id=\"165\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"166\"/>\n              </Account>\n              <Account id=\"167\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Social Security\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"168\"/>\n                <securities id=\"169\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"170\"/>\n              </Account>\n              <Account id=\"171\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"State\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"172\"/>\n                <securities id=\"173\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"174\"/>\n              </Account>\n            </children>\n            <securities id=\"175\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"176\"/>\n          </Account>\n        </children>\n        <securities id=\"177\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"178\"/>\n      </Account>\n      <Account id=\"179\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Income Accounts\" description=\"Income Accounts\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"181\">\n          <Account id=\"182\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Income - Non taxable\" description=\"Non taxable income\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"183\"/>\n            <securities id=\"184\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"185\"/>\n          </Account>\n          <Account id=\"186\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Interest\" description=\"Interest Income\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"187\"/>\n            <securities id=\"188\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"189\"/>\n          </Account>\n          <Account id=\"190\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Other Income\" description=\"Other\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"191\"/>\n            <securities id=\"192\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"193\"/>\n          </Account>\n          <Account id=\"194\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Salary\" description=\"Salary\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"195\"/>\n            <securities id=\"196\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"197\"/>\n          </Account>\n        </children>\n        <securities id=\"198\"/>\n        <accountNumber>95</accountNumber>\n        <propertyMap id=\"199\"/>\n      </Account>\n      <Account id=\"200\" placeHolder=\"false\" locked=\"false\" visible=\"false\" name=\"Opening Balances\" description=\"Opening Balances\">\n        <accountType>EQUITY</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"202\"/>\n        <securities id=\"203\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"204\"/>\n      </Account>\n    </children>\n    <securities id=\"205\"/>\n    <propertyMap id=\"206\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/en/set.txt",
    "content": "common.xml\r\nspouse.xml\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/en/spouse.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Spouse&apos;s Income\" description=\"Spouse&apos;s Income\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"en_US\" symbol=\"USD\" scale=\"2\" prefix=\"$\" suffix=\"\" description=\"en_US\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Expense Accounts\" description=\"Expense Accounts\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Spouse Taxes\" description=\"Spouse&apos;s Taxes\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\">\n              <Account id=\"10\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"County\" description=\"County tax\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"11\"/>\n                <securities id=\"12\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"13\"/>\n              </Account>\n              <Account id=\"14\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Medicare\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"15\"/>\n                <securities id=\"16\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"17\"/>\n              </Account>\n              <Account id=\"18\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Social Security\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"19\"/>\n                <securities id=\"20\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"21\"/>\n              </Account>\n              <Account id=\"22\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"State\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"23\"/>\n                <securities id=\"24\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"25\"/>\n              </Account>\n            </children>\n            <securities id=\"26\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"27\"/>\n          </Account>\n        </children>\n        <securities id=\"28\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"29\"/>\n      </Account>\n      <Account id=\"30\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Income Accounts\" description=\"Income Accounts\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"32\">\n          <Account id=\"33\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Spouse Income\" description=\"Spouse&apos;s Income Accounts\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"30\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"34\">\n              <Account id=\"35\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Income - Non taxable\" description=\"Non taxable income\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"36\"/>\n                <securities id=\"37\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"38\"/>\n              </Account>\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Interest\" description=\"Interest Income\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Other Income\" description=\"Other\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n              <Account id=\"47\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Salary\" description=\"Salary\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"48\"/>\n                <securities id=\"49\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"50\"/>\n              </Account>\n            </children>\n            <securities id=\"51\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"52\"/>\n          </Account>\n        </children>\n        <securities id=\"53\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"54\"/>\n      </Account>\n    </children>\n    <securities id=\"55\"/>\n    <propertyMap id=\"56\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/pt/common.xml",
    "content": "<object-stream>\r\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Contas comuns\" description=\"Contas comuns\">\r\n    <accountType>ROOT</accountType>\r\n    <notes></notes>\r\n    <currencyNode id=\"3\" locale=\"pt_BR\" symbol=\"BRL\" scale=\"2\" prefix=\"R$\" suffix=\"\" description=\"pt_BR\"/>\r\n    <children id=\"4\">\r\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Contas Bancárias\" description=\"\">\r\n        <accountType>BANK</accountType>\r\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\r\n        <notes></notes>\r\n        <currencyNode reference=\"3\"/>\r\n        <children id=\"7\">\r\n          <Account id=\"8\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Dinheiro\" description=\"\">\r\n            <accountType>BANK</accountType>\r\n            <parentAccount reference=\"5\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"9\"/>\r\n            <securities id=\"10\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"11\"/>\r\n          </Account>\r\n          <Account id=\"12\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Economias\" description=\"\">\r\n            <accountType>BANK</accountType>\r\n            <parentAccount reference=\"5\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"13\"/>\r\n            <securities id=\"14\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"15\"/>\r\n          </Account>\r\n        </children>\r\n        <securities id=\"16\"/>\r\n        <accountNumber></accountNumber>\r\n        <propertyMap id=\"17\"/>\r\n      </Account>\r\n      <Account id=\"18\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Contas de Despesas\" description=\"\">\r\n        <accountType>EXPENSE</accountType>\r\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\r\n        <notes></notes>\r\n        <currencyNode reference=\"3\"/>\r\n        <children id=\"20\">\r\n          <Account id=\"21\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Automóvel\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"22\">\r\n              <Account id=\"23\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Combustível\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"21\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"24\"/>\r\n                <securities id=\"25\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"26\"/>\r\n              </Account>\r\n              <Account id=\"27\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Seguro\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"21\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"28\"/>\r\n                <securities id=\"29\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"30\"/>\r\n              </Account>\r\n              <Account id=\"31\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Serviços\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"21\"/>\r\n                <notes>Óleo, pneus, etc.</notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"32\"/>\r\n                <securities id=\"33\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"34\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"35\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"36\"/>\r\n          </Account>\r\n          <Account id=\"37\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Contas\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"38\">\r\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Telefone celular\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"37\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"40\"/>\r\n                <securities id=\"41\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"42\"/>\r\n              </Account>\r\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Telefone\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"37\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"44\"/>\r\n                <securities id=\"45\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"46\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"47\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"48\"/>\r\n          </Account>\r\n          <Account id=\"49\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Alimentação\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"50\">              \r\n              <Account id=\"51\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Refeições fora\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"49\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"52\"/>\r\n                <securities id=\"53\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"54\"/>\r\n              </Account>\r\n              <Account id=\"55\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Guloseimas\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"49\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"56\"/>\r\n                <securities id=\"57\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"58\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"59\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"60\"/>\r\n          </Account>\r\n          <Account id=\"61\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Saúde\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"62\">\r\n              <Account id=\"63\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Plano de saúde\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"61\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"64\"/>\r\n                <securities id=\"65\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"66\"/>\r\n              </Account>\r\n              <Account id=\"67\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Consultas\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"61\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"68\"/>\r\n                <securities id=\"69\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"70\"/>\r\n              </Account>\r\n              <Account id=\"71\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Medicamentos\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"61\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"72\"/>\r\n                <securities id=\"73\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"74\"/>\r\n              </Account>\r\n              <Account id=\"75\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Escritório\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"61\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"76\"/>\r\n                <securities id=\"77\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"78\"/>\r\n              </Account>\r\n              <Account id=\"79\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Bem-estar\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"61\"/>\r\n                <notes>Hait cuts, etc.</notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"80\"/>\r\n                <securities id=\"81\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"82\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"83\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"84\"/>\r\n          </Account>\r\n          <Account id=\"85\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Casa\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"86\">\r\n              <Account id=\"87\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Eletrodomésticos e móveis\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"85\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"88\"/>\r\n                <securities id=\"89\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"90\"/>\r\n              </Account>\r\n              <Account id=\"91\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Vestuário, cama, mesa e banho\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"85\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"92\"/>\r\n                <securities id=\"93\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"94\"/>\r\n              </Account>\r\n              <Account id=\"95\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Eletrônicos\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"85\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"96\"/>\r\n                <securities id=\"97\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"98\"/>\r\n              </Account>\r\n              <Account id=\"99\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Outras\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"85\"/>\r\n                <notes>Selos, baterias, etc</notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"100\"/>\r\n                <securities id=\"101\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"102\"/>\r\n              </Account>\r\n              <Account id=\"103\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Higiene pessoal\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"85\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"104\"/>\r\n                <securities id=\"105\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"106\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"107\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"108\"/>\r\n          </Account>\r\n          <Account id=\"109\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Outras\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"110\">\r\n              <Account id=\"111\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Taxas bancárias\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"109\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"112\"/>\r\n                <securities id=\"113\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"114\"/>\r\n              </Account>\r\n              <Account id=\"115\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Contribuição a caridade\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"109\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"116\"/>\r\n                <securities id=\"117\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"118\"/>\r\n              </Account>\r\n              <Account id=\"119\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Seguro de vida\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"109\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"120\"/>\r\n                <securities id=\"121\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"122\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"123\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"124\"/>\r\n          </Account>\r\n          <Account id=\"125\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Recreação\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"126\">\r\n              <Account id=\"127\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Livros e revistas\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"125\"/>\r\n                <notes>Livros e revistas</notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"128\"/>\r\n                <securities id=\"129\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"130\"/>\r\n              </Account>\r\n              <Account id=\"131\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Concertos / Filmes\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"125\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"132\"/>\r\n                <securities id=\"133\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"134\"/>\r\n              </Account>\r\n              <Account id=\"135\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Passatempo\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"125\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"136\"/>\r\n                <securities id=\"137\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"138\"/>\r\n              </Account>\r\n              <Account id=\"139\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Hospedagem\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"125\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"140\"/>\r\n                <securities id=\"141\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"142\"/>\r\n              </Account>\r\n              <Account id=\"143\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Outras\" description=\"Outras\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"125\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"144\"/>\r\n                <securities id=\"145\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"146\"/>\r\n              </Account>\r\n              <Account id=\"147\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Assinaturas\" description=\"\">\r\n                <accountType>EXPENSE</accountType>\r\n                <parentAccount reference=\"125\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"148\"/>\r\n                <securities id=\"149\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"150\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"151\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"152\"/>\r\n          </Account>\r\n          <Account id=\"153\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Imposto de renda\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"18\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <securities id=\"175\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"176\"/>\r\n          </Account>\r\n        </children>\r\n        <securities id=\"177\"/>\r\n        <accountNumber></accountNumber>\r\n        <propertyMap id=\"178\"/>\r\n      </Account>\r\n      <Account id=\"179\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Contas de Receitas\" description=\"\">\r\n        <accountType>INCOME</accountType>\r\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\r\n        <notes></notes>\r\n        <currencyNode reference=\"3\"/>\r\n        <children id=\"181\">\r\n          <Account id=\"182\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Receitas não tributáveis\" description=\"\">\r\n            <accountType>INCOME</accountType>\r\n            <parentAccount reference=\"179\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"183\"/>\r\n            <securities id=\"184\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"185\"/>\r\n          </Account>\r\n          <Account id=\"186\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Juros\" description=\"\">\r\n            <accountType>INCOME</accountType>\r\n            <parentAccount reference=\"179\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"187\"/>\r\n            <securities id=\"188\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"189\"/>\r\n          </Account>\r\n          <Account id=\"190\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Outras receitas\" description=\"\">\r\n            <accountType>INCOME</accountType>\r\n            <parentAccount reference=\"179\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"191\"/>\r\n            <securities id=\"192\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"193\"/>\r\n          </Account>\r\n          <Account id=\"194\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Salário\" description=\"\">\r\n            <accountType>INCOME</accountType>\r\n            <parentAccount reference=\"179\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"195\"/>\r\n            <securities id=\"196\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"197\"/>\r\n          </Account>\r\n        </children>\r\n        <securities id=\"198\"/>\r\n        <accountNumber>95</accountNumber>\r\n        <propertyMap id=\"199\"/>\r\n      </Account>\r\n      <Account id=\"200\" placeHolder=\"false\" locked=\"false\" visible=\"false\" name=\"Balanço inicial\" description=\"\">\r\n        <accountType>EQUITY</accountType>\r\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\r\n        <notes></notes>\r\n        <currencyNode reference=\"3\"/>\r\n        <children id=\"202\"/>\r\n        <securities id=\"203\"/>\r\n        <accountNumber></accountNumber>\r\n        <propertyMap id=\"204\"/>\r\n      </Account>\r\n    </children>\r\n    <securities id=\"205\"/>\r\n    <propertyMap id=\"206\"/>\r\n  </RootAccount>\r\n</object-stream>\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/pt/set.txt",
    "content": "common.xml\nspouse.xml\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/pt/spouse.xml",
    "content": "<object-stream>\r\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Receitas da esposa\" description=\"\">\r\n    <accountType>ROOT</accountType>\r\n    <notes></notes>\r\n    <currencyNode id=\"3\" locale=\"pt_BR\" symbol=\"BRL\" scale=\"2\" prefix=\"R$\" suffix=\"\" description=\"pt_BR\"/>\r\n    <children id=\"4\">\r\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Contas de Depesas\" description=\"\">\r\n        <accountType>EXPENSE</accountType>\r\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\r\n        <notes></notes>\r\n        <currencyNode reference=\"3\"/>\r\n        <children id=\"7\">\r\n          <Account id=\"8\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Imposto de renda esposa\" description=\"\">\r\n            <accountType>EXPENSE</accountType>\r\n            <parentAccount reference=\"5\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <securities id=\"26\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"27\"/>\r\n          </Account>\r\n        </children>\r\n        <securities id=\"28\"/>\r\n        <accountNumber></accountNumber>\r\n        <propertyMap id=\"29\"/>\r\n      </Account>\r\n      <Account id=\"30\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Contas de Receitas\" description=\"\">\r\n        <accountType>INCOME</accountType>\r\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\r\n        <notes></notes>\r\n        <currencyNode reference=\"3\"/>\r\n        <children id=\"32\">\r\n          <Account id=\"33\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Receitas da esposa\" description=\"\">\r\n            <accountType>INCOME</accountType>\r\n            <parentAccount reference=\"30\"/>\r\n            <notes></notes>\r\n            <currencyNode reference=\"3\"/>\r\n            <children id=\"34\">\r\n              <Account id=\"35\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Receitas não tributáveis\" description=\"\">\r\n                <accountType>INCOME</accountType>\r\n                <parentAccount reference=\"33\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"36\"/>\r\n                <securities id=\"37\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"38\"/>\r\n              </Account>\r\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Juros\" description=\"\">\r\n                <accountType>INCOME</accountType>\r\n                <parentAccount reference=\"33\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"40\"/>\r\n                <securities id=\"41\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"42\"/>\r\n              </Account>\r\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Outras receitas\" description=\"\">\r\n                <accountType>INCOME</accountType>\r\n                <parentAccount reference=\"33\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"44\"/>\r\n                <securities id=\"45\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"46\"/>\r\n              </Account>\r\n              <Account id=\"47\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Salário\" description=\"\">\r\n                <accountType>INCOME</accountType>\r\n                <parentAccount reference=\"33\"/>\r\n                <notes></notes>\r\n                <currencyNode reference=\"3\"/>\r\n                <children id=\"48\"/>\r\n                <securities id=\"49\"/>\r\n                <accountNumber></accountNumber>\r\n                <propertyMap id=\"50\"/>\r\n              </Account>\r\n            </children>\r\n            <securities id=\"51\"/>\r\n            <accountNumber></accountNumber>\r\n            <propertyMap id=\"52\"/>\r\n          </Account>\r\n        </children>\r\n        <securities id=\"53\"/>\r\n        <accountNumber></accountNumber>\r\n        <propertyMap id=\"54\"/>\r\n      </Account>\r\n    </children>\r\n    <securities id=\"55\"/>\r\n    <propertyMap id=\"56\"/>\r\n  </RootAccount>\r\n</object-stream>\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/zh/common.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"常用账户\" description=\"Common Accounts\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"zh_CN\" symbol=\"RMB\" scale=\"2\" prefix=\"￥\" suffix=\"\" description=\"zh_CN\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"银行类\" description=\"Bank Accounts\">\n        <accountType>BANK</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"现金\" description=\"Cash Account\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\"/>\n            <securities id=\"10\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"11\"/>\n          </Account>\n          <Account id=\"12\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"储蓄\" description=\"Savings Account\">\n            <accountType>BANK</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"13\"/>\n            <securities id=\"14\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"15\"/>\n          </Account>\n        </children>\n        <securities id=\"16\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"17\"/>\n      </Account>\n      <Account id=\"18\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"支出类\" description=\"Expense Accounts\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"20\">\n          <Account id=\"21\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"车辆类\" description=\"Description\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"22\">\n              <Account id=\"23\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"汽油\" description=\"Fuel Expenses\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"24\"/>\n                <securities id=\"25\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"26\"/>\n              </Account>\n              <Account id=\"27\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"车险\" description=\"Auto Insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"28\"/>\n                <securities id=\"29\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"30\"/>\n              </Account>\n              <Account id=\"31\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"保养维护\" description=\"Auto Services\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"21\"/>\n                <notes>Oil, tires, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"32\"/>\n                <securities id=\"33\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"34\"/>\n              </Account>\n            </children>\n            <securities id=\"35\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"36\"/>\n          </Account>\n          <Account id=\"37\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"账单类\" description=\"Bills\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"38\">\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"手机\" description=\"Cell Phone\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"固定电话\" description=\"Telephone\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"37\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n            </children>\n            <securities id=\"47\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"48\"/>\n          </Account>\n          <Account id=\"49\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"食物\" description=\"Food\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"50\">              \n              <Account id=\"51\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"正餐\" description=\"Dining Out\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"52\"/>\n                <securities id=\"53\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"54\"/>\n              </Account>\n              <Account id=\"55\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"杂货\" description=\"Groceries\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"49\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"56\"/>\n                <securities id=\"57\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"58\"/>\n              </Account>\n            </children>\n            <securities id=\"59\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"60\"/>\n          </Account>\n          <Account id=\"61\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"健康医疗类\" description=\"Health\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"62\">\n              <Account id=\"63\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"医疗保险\" description=\"Medical insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"64\"/>\n                <securities id=\"65\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"66\"/>\n              </Account>\n              <Account id=\"67\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"大宗医疗费用\" description=\"Major Medical Expense\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"68\"/>\n                <securities id=\"69\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"70\"/>\n              </Account>\n              <Account id=\"71\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"药物药剂\" description=\"Medication\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"72\"/>\n                <securities id=\"73\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"74\"/>\n              </Account>\n              <Account id=\"75\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"诊所就医\" description=\"Office Visits\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"76\"/>\n                <securities id=\"77\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"78\"/>\n              </Account>\n              <Account id=\"79\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"健身\" description=\"Wellness\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"61\"/>\n                <notes>Hait cuts, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"80\"/>\n                <securities id=\"81\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"82\"/>\n              </Account>\n            </children>\n            <securities id=\"83\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"84\"/>\n          </Account>\n          <Account id=\"85\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"家务类\" description=\"Houseold\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"86\">\n              <Account id=\"87\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"家具饰品\" description=\"Appliances and Furniture\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"88\"/>\n                <securities id=\"89\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"90\"/>\n              </Account>\n              <Account id=\"91\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"衣物寝具\" description=\"Clothing and Bedding\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"92\"/>\n                <securities id=\"93\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"94\"/>\n              </Account>\n              <Account id=\"95\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"电器\" description=\"Electronics for the house\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"96\"/>\n                <securities id=\"97\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"98\"/>\n              </Account>\n              <Account id=\"99\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"其他耗材\" description=\"Misc Consumables\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes>Stamps, batteries, etc</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"100\"/>\n                <securities id=\"101\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"102\"/>\n              </Account>\n              <Account id=\"103\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"厕卫用品\" description=\"Toiletries\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"85\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"104\"/>\n                <securities id=\"105\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"106\"/>\n              </Account>\n            </children>\n            <securities id=\"107\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"108\"/>\n          </Account>\n          <Account id=\"109\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"未分类\" description=\"Miscellaneous\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"110\">\n              <Account id=\"111\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"银行手续费\" description=\"Bank Fees\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes>ATM Fees, OD protection, etc.</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"112\"/>\n                <securities id=\"113\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"114\"/>\n              </Account>\n              <Account id=\"115\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"慈善捐助\" description=\"Charitable Contributions\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"116\"/>\n                <securities id=\"117\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"118\"/>\n              </Account>\n              <Account id=\"119\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"人寿保险\" description=\"Life Insurance\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"109\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"120\"/>\n                <securities id=\"121\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"122\"/>\n              </Account>\n            </children>\n            <securities id=\"123\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"124\"/>\n          </Account>\n          <Account id=\"125\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"消遣类\" description=\"\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"126\">\n              <Account id=\"127\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"书籍杂志\" description=\"Books and Magazines\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes>Books and magzines</notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"128\"/>\n                <securities id=\"129\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"130\"/>\n              </Account>\n              <Account id=\"131\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"电影及演出\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"132\"/>\n                <securities id=\"133\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"134\"/>\n              </Account>\n              <Account id=\"135\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"爱好\" description=\"Misc Hobbies\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"136\"/>\n                <securities id=\"137\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"138\"/>\n              </Account>\n              <Account id=\"139\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"住宿\" description=\"Hotels / Etc.\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"140\"/>\n                <securities id=\"141\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"142\"/>\n              </Account>\n              <Account id=\"143\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"其他杂项\" description=\"Misc\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"144\"/>\n                <securities id=\"145\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"146\"/>\n              </Account>\n              <Account id=\"147\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"订阅\" description=\"Magazine Subscriptions\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"125\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"148\"/>\n                <securities id=\"149\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"150\"/>\n              </Account>\n            </children>\n            <securities id=\"151\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"152\"/>\n          </Account>\n          <Account id=\"153\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"税费类\" description=\"Description\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"18\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"154\">\n              <Account id=\"155\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"其他税费\" description=\"County tax\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"156\"/>\n                <securities id=\"157\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"158\"/>\n              </Account>\n              <Account id=\"159\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"国税\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"160\"/>\n                <securities id=\"161\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"162\"/>\n              </Account>\n              <Account id=\"163\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"医疗保险\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"164\"/>\n                <securities id=\"165\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"166\"/>\n              </Account>\n              <Account id=\"167\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"社会保险\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"168\"/>\n                <securities id=\"169\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"170\"/>\n              </Account>\n              <Account id=\"171\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"地税\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"153\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"172\"/>\n                <securities id=\"173\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"174\"/>\n              </Account>\n            </children>\n            <securities id=\"175\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"176\"/>\n          </Account>\n        </children>\n        <securities id=\"177\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"178\"/>\n      </Account>\n      <Account id=\"179\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"收入类\" description=\"Income Accounts\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"181\">\n          <Account id=\"182\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"免税收入\" description=\"Non taxable income\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"183\"/>\n            <securities id=\"184\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"185\"/>\n          </Account>\n          <Account id=\"186\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"利息\" description=\"Interest Income\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"187\"/>\n            <securities id=\"188\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"189\"/>\n          </Account>\n          <Account id=\"190\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"其他收入\" description=\"Other\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"191\"/>\n            <securities id=\"192\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"193\"/>\n          </Account>\n          <Account id=\"194\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"薪水\" description=\"Salary\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"179\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"195\"/>\n            <securities id=\"196\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"197\"/>\n          </Account>\n        </children>\n        <securities id=\"198\"/>\n        <accountNumber>95</accountNumber>\n        <propertyMap id=\"199\"/>\n      </Account>\n      <Account id=\"200\" placeHolder=\"false\" locked=\"false\" visible=\"false\" name=\"期初余额\" description=\"Opening Balances\">\n        <accountType>EQUITY</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"202\"/>\n        <securities id=\"203\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"204\"/>\n      </Account>\n    </children>\n    <securities id=\"205\"/>\n    <propertyMap id=\"206\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/zh/set.txt",
    "content": "common.xml\r\nspouse.xml\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/account/zh/spouse.xml",
    "content": "<object-stream>\n  <RootAccount id=\"1\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"配偶的收入\" description=\"Spouse&apos;s Income\">\n    <accountType>ROOT</accountType>\n    <notes></notes>\n    <currencyNode id=\"3\" locale=\"zh_CN\" symbol=\"RMB\" scale=\"2\" prefix=\"￥\" suffix=\"\" description=\"zh_CN\"/>\n    <children id=\"4\">\n      <Account id=\"5\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"支出类\" description=\"Expense Accounts\">\n        <accountType>EXPENSE</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"7\">\n          <Account id=\"8\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"配偶的税费\" description=\"Spouse&apos;s Taxes\">\n            <accountType>EXPENSE</accountType>\n            <parentAccount reference=\"5\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"9\">\n              <Account id=\"10\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"其他税费\" description=\"County tax\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"11\"/>\n                <securities id=\"12\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"13\"/>\n              </Account>\n              <Account id=\"14\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"医疗保险\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"15\"/>\n                <securities id=\"16\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"17\"/>\n              </Account>\n              <Account id=\"18\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"社会保险\" description=\"\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"19\"/>\n                <securities id=\"20\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"21\"/>\n              </Account>\n              <Account id=\"22\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"地税\" description=\"Description\">\n                <accountType>EXPENSE</accountType>\n                <parentAccount reference=\"8\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"23\"/>\n                <securities id=\"24\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"25\"/>\n              </Account>\n            </children>\n            <securities id=\"26\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"27\"/>\n          </Account>\n        </children>\n        <securities id=\"28\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"29\"/>\n      </Account>\n      <Account id=\"30\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"收入类\" description=\"Income Accounts\">\n        <accountType>INCOME</accountType>\n        <parentAccount class=\"RootAccount\" reference=\"1\"/>\n        <notes></notes>\n        <currencyNode reference=\"3\"/>\n        <children id=\"32\">\n          <Account id=\"33\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"配偶的收入\" description=\"Spouse&apos;s Income Accounts\">\n            <accountType>INCOME</accountType>\n            <parentAccount reference=\"30\"/>\n            <notes></notes>\n            <currencyNode reference=\"3\"/>\n            <children id=\"34\">\n              <Account id=\"35\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"免税收入\" description=\"Non taxable income\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"36\"/>\n                <securities id=\"37\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"38\"/>\n              </Account>\n              <Account id=\"39\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"利息\" description=\"Interest Income\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"40\"/>\n                <securities id=\"41\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"42\"/>\n              </Account>\n              <Account id=\"43\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"其他收入\" description=\"Other\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"44\"/>\n                <securities id=\"45\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"46\"/>\n              </Account>\n              <Account id=\"47\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"薪水\" description=\"Salary\">\n                <accountType>INCOME</accountType>\n                <parentAccount reference=\"33\"/>\n                <notes></notes>\n                <currencyNode reference=\"3\"/>\n                <children id=\"48\"/>\n                <securities id=\"49\"/>\n                <accountNumber></accountNumber>\n                <propertyMap id=\"50\"/>\n              </Account>\n            </children>\n            <securities id=\"51\"/>\n            <accountNumber></accountNumber>\n            <propertyMap id=\"52\"/>\n          </Account>\n        </children>\n        <securities id=\"53\"/>\n        <accountNumber></accountNumber>\n        <propertyMap id=\"54\"/>\n      </Account>\n    </children>\n    <securities id=\"55\"/>\n    <propertyMap id=\"56\"/>\n  </RootAccount>\n</object-stream>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/constants.properties",
    "content": "build.date   = ${timeStamp}\nbuild.system = ${osName}\nbuild.jvm = ${javaVersion}\nname = jGnash\nversion = ${version}\n\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/apache-license.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML lang=\"en\">\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=iso-8859-1\">\n\t<TITLE></TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 2.2  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20070915;7395700\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20070915;7402300\">\n</HEAD>\n<BODY LANG=\"en-US\" DIR=\"LTR\">\n<PRE>\n                                 <b>Apache License</b>\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      &quot;License&quot; shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      &quot;Legal Entity&quot; shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      &quot;control&quot; means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      &quot;Source&quot; form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      &quot;Object&quot; form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      &quot;Work&quot; shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      &quot;Derivative Works&quot; shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      &quot;Contribution&quot; shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, &quot;submitted&quot;\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as &quot;Not a Contribution.&quot;\n\n      &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a &quot;NOTICE&quot; text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n</PRE><P ALIGN=LEFT STYLE=\"margin-bottom: 0\">\n<BR>\n</P>\n<P><BR><BR>\n</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/credits.html",
    "content": "<html lang=\"en\">\n    <head>\n    <title></title>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n        <style>\n            table, th, td {\n                border: 1px solid black;\n            }\n            th, td {\n                padding: 5px;\n            }\n\n            body {\n                color: black;\n                background-color: white;\n            }\n        </style>\n  </head>\n<body>\n<H1>jGnash</H1>\n<pre>Copyright (C) 2001-2021 Craig Cavanaugh</pre>\n\n<H2>Contributers</H2>\nNavneet Karnani<br>\nTed Stockwell<br>\nJoe Cooper<br>\nBob White<br>\nJens M&uuml;hlenhoff (felrond)<br>\nKeith Winston<br>\nDon Brown<br>\nTheodore Poe<br>\nBradley Smith<br>\nStefan Wundrak<br>\nJoe Planisky<br>\nJay Dolan<br>\nLeonard Siu (axnotizes)<br>\nJeff Prickett<br>\nBruce Patin<br>\nKevin Kingsbury<br>\nBryan Lin<br>\nFran&#231;ois Pegory<br>\nSteve Scalia<br>\nChad McHenry<br>\nDavid Robertson<br>\nAleksey Trufanov<br>\nMatheus Antonelli<br>\nKarim Samir<br>\nThomas McManus<br>\nTom Edelson<br>\nClemens Wacha<br>\nJames Morrissey<br>\nVincent Frison<br>\nDany Veilleux<br>\nChris Medinger<br>\nilanbe<br>\nJuan Jos&eacute Garc&iacute;a Ripoll<br>\nNicolas Bouillon<br>\nMarcelo Abelda&ntilde;o<br>\nAndrey Bondarenko<br>\nRob Hills<br>\nAdrian Gygax<br>\nPeter Vida<br>\nVitaliy Aksyonov<br>\nAdrian A<br>\nNathan McCrina<br>\nLubo&#353; Hilgert<br>\nChris Bunney<br>\nhellemans<br>\nGertjan Idema<br>\nPranay Kumar<br>\nAlex Werz_\n\n<H2>Language Translation</H2>\n\n<H3>Simplified Chinese</H3>\nBryan Lin\n<H3>French</H3>\nR&#233;gis Latawiec<br>\nClosen<br>\nFran&#231;ois Pegory<br>\nVincent Frison\n<H3>German</H3>\nJens M&uuml;hlenhoff (felrond)<br>\nKarim Samir<br>\nAdrian Gygax<br>\nAlex Werz\n<H3>Italian</H3>\nMassimo Ferrari<br>\nDavide\n<H3>Polish</H3>\nSławomir Szarkowicz<br>\n<H3>Portuguese</H3>\nHenrique Meira<br>\nMatheus Antonelli<br>\nPietro Augusto<br>\nMarco A L Barbosa<br>\nPietro A R Cerchiart<br>\nFernando Ribeiro da Silva\n<H3>Lituanian</H3>\nAurimas Butkus\n<H3>Russian</H3>\nAleksey Trufanov<br>\npchurzin\n<H3>Spanish</H3>\nJuan Jos&eacute Garc&iacute;a Ripoll<br>\nLenin Torres<br>\nMarcelo Abelda&ntilde;o\n<H3>English (United Kingdom)</H3>\nJames Morrissey\n<H3>Dutch</H3>\nCarl van Denzen<br>\nhellemans\n<H3>Hebrew</H3>\nilanbe\n<H3>Ukrainian</H3>\nVitaliy Aksyonov\n<H3>Czech</H3>\nLubo&#353; Hilgert\n\n<H2>External Libraries</H2>\n\n<table>\n    <tr>\n        <td><STRONG>Library</STRONG></td>\n        <td><STRONG>License</STRONG></td>\n        <td><STRONG>Home Page</STRONG></td>\n    </tr>\n    <tr>\n        <td>picocli</td>\n        <td>Apache License Version 2.0</td>\n        <td>https://github.com/remkop/picocli</td>\n    </tr>\n    <tr>\n        <td>Netty</td>\n        <td>Apache License Version 2.0</td>\n        <td>https://netty.io/</td>\n    </tr>\n    <tr>\n        <td>XStream</td>\n        <td>BSD</td>\n        <td>https://github.com/x-stream/xstream</td>\n    </tr>\n    <tr>\n        <td>Hibernate ORM</td>\n        <td>LPGL 2.1</td>\n        <td>http://hibernate.org/orm/</td>\n    </tr>\n    <tr>\n        <td>H2 Database Engine</td>\n        <td>MPL 2.0 / EPL 1.0</td>\n        <td>www.h2database.com/html/main.html</td>\n    </tr>\n    <tr>\n        <td>HSQLDB / HyperSQL</td>\n        <td>BSD</td>\n        <td>http://hsqldb.org/</td>\n    </tr>\n    <tr>\n        <td>Apache PDFBox</td>\n        <td>Apache License Version 2.0</td>\n        <td>https://pdfbox.apache.org/</td>\n    </tr>\n    <tr>\n        <td>Apache POI</td>\n        <td>Apache License Version 2.0</td>\n        <td>https://poi.apache.org/</td>\n    </tr>\n    <tr>\n        <td>HikariCP</td>\n        <td>Apache License Version 2.0</td>\n        <td>https://github.com/brettwooldridge/HikariCP</td>\n    </tr>\n</table>\n\n</body>\n</html>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/gpl-license.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<html lang=\"en\">\n<head>\n   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n   <meta name=\"GENERATOR\" content=\"Mozilla/4.77 [en] (X11; U; Linux 2.4.5-ac2 i686) [Netscape]\">\n   <title>GNU General Public License - GNU Project - Free Software Foundation (FSF)</title>\n<link REV=\"made\" HREF=\"mailto:webmasters@www.gnu.org\">\n</head>\n<body>\n\n<h2>\n<a href=\"http://www.gnu.org/copyleft/gpl.html#TOC1\" NAME=\"SEC1\">GNU GENERAL\nPUBLIC LICENSE</a></h2>\nVersion 2, June 1991\n<pre>Copyright (C) 1989, 1991 Free Software Foundation, Inc.&nbsp;&nbsp;\n59 Temple Place - Suite 330, Boston, MA&nbsp; 02111-1307, USA\n\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.</pre>\n\n<h2>\n<a href=\"http://www.gnu.org/copyleft/gpl.html#TOC2\" NAME=\"SEC2\">Preamble</a></h2>\nThe licenses for most software are designed to take away your freedom to\nshare and change it. By contrast, the GNU General Public License is intended\nto guarantee your freedom to share and change free software--to make sure\nthe software is free for all its users. This General Public License applies\nto most of the Free Software Foundation's software and to any other program\nwhose authors commit to using it. (Some other Free Software Foundation\nsoftware is covered by the GNU Library General Public License instead.)\nYou can apply it to your programs, too.\n<p>When we speak of free software, we are referring to freedom, not price.\nOur General Public Licenses are designed to make sure that you have the\nfreedom to distribute copies of free software (and charge for this service\nif you wish), that you receive source code or can get it if you want it,\nthat you can change the software or use pieces of it in new free programs;\nand that you know you can do these things.\n<p>To protect your rights, we need to make restrictions that forbid anyone\nto deny you these rights or to ask you to surrender the rights. These restrictions\ntranslate to certain responsibilities for you if you distribute copies\nof the software, or if you modify it.\n<p>For example, if you distribute copies of such a program, whether gratis\nor for a fee, you must give the recipients all the rights that you have.\nYou must make sure that they, too, receive or can get the source code.\nAnd you must show them these terms so they know their rights.\n<p>We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy, distribute\nand/or modify the software.\n<p>Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free software.\nIf the software is modified by someone else and passed on, we want its\nrecipients to know that what they have is not the original, so that any\nproblems introduced by others will not reflect on the original authors'\nreputations.\n<p>Finally, any free program is threatened constantly by software patents.\nWe wish to avoid the danger that redistributors of a free program will\nindividually obtain patent licenses, in effect making the program proprietary.\nTo prevent this, we have made it clear that any patent must be licensed\nfor everyone's free use or not licensed at all.\n<p>The precise terms and conditions for copying, distribution and modification\nfollow.\n<h2>\n<a href=\"http://www.gnu.org/copyleft/gpl.html#TOC3\" NAME=\"SEC3\">TERMS AND\nCONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h2>\n<b>0.</b> This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed under\nthe terms of this General Public License. The \"Program\", below, refers\nto any such program or work, and a \"work based on the Program\" means either\nthe Program or any derivative work under copyright law: that is to say,\na work containing the Program or a portion of it, either verbatim or with\nmodifications and/or translated into another language. (Hereinafter, translation\nis included without limitation in the term \"modification\".) Each licensee\nis addressed as \"you\".\n<p>Activities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope. The act of running\nthe Program is not restricted, and the output from the Program is covered\nonly if its contents constitute a work based on the Program (independent\nof having been made by running the Program). Whether that is true depends\non what the Program does.\n<p><b>1.</b> You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you conspicuously\nand appropriately publish on each copy an appropriate copyright notice\nand disclaimer of warranty; keep intact all the notices that refer to this\nLicense and to the absence of any warranty; and give any other recipients\nof the Program a copy of this License along with the Program.\n<p>You may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n<p><b>2.</b> You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and distribute\nsuch modifications or work under the terms of Section 1 above, provided\nthat you also meet all of these conditions:\n<ul>\n<li>\n<b>a)</b> You must cause the modified files to carry prominent notices\nstating that you changed the files and the date of any change.</li>\n\n<li>\n<b>b)</b> You must cause any work that you distribute or publish, that\nin whole or in part contains or is derived from the Program or any part\nthereof, to be licensed as a whole at no charge to all third parties under\nthe terms of this License.</li>\n\n<li>\n<b>c)</b> If the modified program normally reads commands interactively\nwhen run, you must cause it, when started running for such interactive\nuse in the most ordinary way, to print or display an announcement including\nan appropriate copyright notice and a notice that there is no warranty\n(or else, saying that you provide a warranty) and that users may redistribute\nthe program under these conditions, and telling the user how to view a\ncopy of this License. (Exception: if the Program itself is interactive\nbut does not normally print such an announcement, your work based on the\nProgram is not required to print an announcement.)</li>\n</ul>\nThese requirements apply to the modified work as a whole. If identifiable\nsections of that work are not derived from the Program, and can be reasonably\nconsidered independent and separate works in themselves, then this License,\nand its terms, do not apply to those sections when you distribute them\nas separate works. But when you distribute the same sections as part of\na whole which is a work based on the Program, the distribution of the whole\nmust be on the terms of this License, whose permissions for other licensees\nextend to the entire whole, and thus to each and every part regardless\nof who wrote it.\n<p>Thus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to exercise\nthe right to control the distribution of derivative or collective works\nbased on the Program.\n<p>In addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of a\nstorage or distribution medium does not bring the other work under the\nscope of this License.\n<p><b>3.</b> You may copy and distribute the Program (or a work based on\nit, under Section 2) in object code or executable form under the terms\nof Sections 1 and 2 above provided that you also do one of the following:\n<ul>\n<li>\n<b>a)</b> Accompany it with the complete corresponding machine-readable\nsource code, which must be distributed under the terms of Sections 1 and\n2 above on a medium customarily used for software interchange; or,</li>\n\n<li>\n<b>b)</b> Accompany it with a written offer, valid for at least three years,\nto give any third party, for a charge no more than your cost of physically\nperforming source distribution, a complete machine-readable copy of the\ncorresponding source code, to be distributed under the terms of Sections\n1 and 2 above on a medium customarily used for software interchange; or,</li>\n\n<li>\n<b>c)</b> Accompany it with the information you received as to the offer\nto distribute corresponding source code. (This alternative is allowed only\nfor noncommercial distribution and only if you received the program in\nobject code or executable form with such an offer, in accord with Subsection\nb above.)</li>\n</ul>\nThe source code for a work means the preferred form of the work for making\nmodifications to it. For an executable work, complete source code means\nall the source code for all modules it contains, plus any associated interface\ndefinition files, plus the scripts used to control compilation and installation\nof the executable. However, as a special exception, the source code distributed\nneed not include anything that is normally distributed (in either source\nor binary form) with the major components (compiler, kernel, and so on)\nof the operating system on which the executable runs, unless that component\nitself accompanies the executable.\n<p>If distribution of executable or object code is made by offering access\nto copy from a designated place, then offering equivalent access to copy\nthe source code from the same place counts as distribution of the source\ncode, even though third parties are not compelled to copy the source along\nwith the object code.\n<p><b>4.</b> You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License. Any attempt otherwise\nto copy, modify, sublicense or distribute the Program is void, and will\nautomatically terminate your rights under this License. However, parties\nwho have received copies, or rights, from you under this License will not\nhave their licenses terminated so long as such parties remain in full compliance.\n<p><b>5.</b> You are not required to accept this License, since you have\nnot signed it. However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works. These actions are prohibited\nby law if you do not accept this License. Therefore, by modifying or distributing\nthe Program (or any work based on the Program), you indicate your acceptance\nof this License to do so, and all its terms and conditions for copying,\ndistributing or modifying the Program or works based on it.\n<p><b>6.</b> Each time you redistribute the Program (or any work based\non the Program), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions. You may not impose any further restrictions\non the recipients' exercise of the rights granted herein. You are not responsible\nfor enforcing compliance by third parties to this License.\n<p><b>7.</b> If, as a consequence of a court judgment or allegation of\npatent infringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or otherwise)\nthat contradict the conditions of this License, they do not excuse you\nfrom the conditions of this License. If you cannot distribute so as to\nsatisfy simultaneously your obligations under this License and any other\npertinent obligations, then as a consequence you may not distribute the\nProgram at all. For example, if a patent license would not permit royalty-free\nredistribution of the Program by all those who receive copies directly\nor indirectly through you, then the only way you could satisfy both it\nand this License would be to refrain entirely from distribution of the\nProgram.\n<p>If any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other circumstances.\n<p>It is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any such\nclaims; this section has the sole purpose of protecting the integrity of\nthe free software distribution system, which is implemented by public license\npractices. Many people have made generous contributions to the wide range\nof software distributed through that system in reliance on consistent application\nof that system; it is up to the author/donor to decide if he or she is\nwilling to distribute software through any other system and a licensee\ncannot impose that choice.\n<p>This section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n<p><b>8.</b> If the distribution and/or use of the Program is restricted\nin certain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License may\nadd an explicit geographical distribution limitation excluding those countries,\nso that distribution is permitted only in or among countries not thus excluded.\nIn such case, this License incorporates the limitation as if written in\nthe body of this License.\n<p><b>9.</b> The Free Software Foundation may publish revised and/or new\nversions of the General Public License from time to time. Such new versions\nwill be similar in spirit to the present version, but may differ in detail\nto address new problems or concerns.\n<p>Each version is given a distinguishing version number. If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free Software\nFoundation. If the Program does not specify a version number of this License,\nyou may choose any version ever published by the Free Software Foundation.\n<p><b>10.</b> If you wish to incorporate parts of the Program into other\nfree programs whose distribution conditions are different, write to the\nauthor to ask for permission. For software which is copyrighted by the\nFree Software Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this. Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n<p><b>NO WARRANTY</b>\n<p><b>11.</b> BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS\nNO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\nEXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER\nPARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER\nEXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK\nAS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n<p><b>12.</b> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO\nIN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\nAND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR\nDAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES\nARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT\nLIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED\nBY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY\nOTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGES.\n<h2>\nEND OF TERMS AND CONDITIONS</h2>\n\n</body>\n</html>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/jgnash-license.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML lang=\"en\">\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=iso-8859-1\">\n\t<TITLE>GNU General Public License - GNU Project - Free Software Foundation (FSF)</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 2.2  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20070914;4495400\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20070914;4563400\">\n\t<STYLE TYPE=\"text/css\">\n\t<!--\n\t\tP { color: #000000 }\n\t\tH3 { color: #000000 }\n\t\tH4 { color: #000000 }\n\t\tA:link { color: #1f00ff }\n\t\tA:visited { color: #9900dd }\n\t-->\n\t</STYLE>\n</HEAD>\n<BODY LANG=\"en-US\" DIR=\"LTR\">\n<H3 ALIGN=CENTER>GNU GENERAL PUBLIC LICENSE</H3>\n<P ALIGN=CENTER>Version 3, 29 June 2007</P>\n<P>Copyright (C) 2007 Free Software Foundation, Inc.\n&lt;http://fsf.org/&gt;</P>\n<P>Everyone is permitted to copy and distribute verbatim copies of\nthis license document, but changing it is not allowed.</P>\n<H3><A NAME=\"preamble\"></A>Preamble</H3>\n<P>The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.</P>\n<P>The licenses for most software and other practical works are\ndesigned to take away your freedom to share and change the works. By\ncontrast, the GNU General Public License is intended to guarantee\nyour freedom to share and change all versions of a program--to make\nsure it remains free software for all its users. We, the Free\nSoftware Foundation, use the GNU General Public License for most of\nour software; it applies also to any other work released this way by\nits authors. You can apply it to your programs, too.</P>\n<P>When we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge\nfor them if you wish), that you receive source code or can get it if\nyou want it, that you can change the software or use pieces of it in\nnew free programs, and that you know you can do these things.</P>\n<P>To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights. Therefore, you\nhave certain responsibilities if you distribute copies of the\nsoftware, or if you modify it: responsibilities to respect the\nfreedom of others.</P>\n<P>For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received. You must make sure that they, too,\nreceive or can get the source code. And you must show them these\nterms so they know their rights.</P>\n<P>Developers that use the GNU GPL protect your rights with two\nsteps: (1) assert copyright on the software, and (2) offer you this\nLicense giving you legal permission to copy, distribute and/or modify\nit.</P>\n<P>For the developers' and authors' protection, the GPL clearly\nexplains that there is no warranty for this free software. For both\nusers' and authors' sake, the GPL requires that modified versions be\nmarked as changed, so that their problems will not be attributed\nerroneously to authors of previous versions.</P>\n<P>Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the\nmanufacturer can do so. This is fundamentally incompatible with the\naim of protecting users' freedom to change the software. The\nsystematic pattern of such abuse occurs in the area of products for\nindividuals to use, which is precisely where it is most unacceptable.\nTherefore, we have designed this version of the GPL to prohibit the\npractice for those products. If such problems arise substantially in\nother domains, we stand ready to extend this provision to those\ndomains in future versions of the GPL, as needed to protect the\nfreedom of users.</P>\n<P>Finally, every program is threatened constantly by software\npatents. States should not allow patents to restrict development and\nuse of software on general-purpose computers, but in those that do,\nwe wish to avoid the special danger that patents applied to a free\nprogram could make it effectively proprietary. To prevent this, the\nGPL assures that patents cannot be used to render the program\nnon-free.</P>\n<P>The precise terms and conditions for copying, distribution and\nmodification follow.</P>\n<H3><A NAME=\"terms\"></A>TERMS AND CONDITIONS</H3>\n<H4><A NAME=\"section0\"></A>0. Definitions.</H4>\n<P>&ldquo;This License&rdquo; refers to version 3 of the GNU General\nPublic License.</P>\n<P>&ldquo;Copyright&rdquo; also means copyright-like laws that apply\nto other kinds of works, such as semiconductor masks.</P>\n<P>&ldquo;The Program&rdquo; refers to any copyrightable work\nlicensed under this License. Each licensee is addressed as &ldquo;you&rdquo;.\n&ldquo;Licensees&rdquo; and &ldquo;recipients&rdquo; may be\nindividuals or organizations.</P>\n<P>To &ldquo;modify&rdquo; a work means to copy from or adapt all or\npart of the work in a fashion requiring copyright permission, other\nthan the making of an exact copy. The resulting work is called a\n&ldquo;modified version&rdquo; of the earlier work or a work &ldquo;based\non&rdquo; the earlier work.</P>\n<P>A &ldquo;covered work&rdquo; means either the unmodified Program\nor a work based on the Program.</P>\n<P>To &ldquo;propagate&rdquo; a work means to do anything with it\nthat, without permission, would make you directly or secondarily\nliable for infringement under applicable copyright law, except\nexecuting it on a computer or modifying a private copy. Propagation\nincludes copying, distribution (with or without modification), making\navailable to the public, and in some countries other activities as\nwell.</P>\n<P>To &ldquo;convey&rdquo; a work means any kind of propagation that\nenables other parties to make or receive copies. Mere interaction\nwith a user through a computer network, with no transfer of a copy,\nis not conveying.</P>\n<P>An interactive user interface displays &ldquo;Appropriate Legal\nNotices&rdquo; to the extent that it includes a convenient and\nprominently visible feature that (1) displays an appropriate\ncopyright notice, and (2) tells the user that there is no warranty\nfor the work (except to the extent that warranties are provided),\nthat licensees may convey the work under this License, and how to\nview a copy of this License. If the interface presents a list of user\ncommands or options, such as a menu, a prominent item in the list\nmeets this criterion.</P>\n<H4><A NAME=\"section1\"></A>1. Source Code.</H4>\n<P>The &ldquo;source code&rdquo; for a work means the preferred form\nof the work for making modifications to it. &ldquo;Object code&rdquo;\nmeans any non-source form of a work.</P>\n<P>A &ldquo;Standard Interface&rdquo; means an interface that either\nis an official standard defined by a recognized standards body, or,\nin the case of interfaces specified for a particular programming\nlanguage, one that is widely used among developers working in that\nlanguage.</P>\n<P>The &ldquo;System Libraries&rdquo; of an executable work include\nanything, other than the work as a whole, that (a) is included in the\nnormal form of packaging a Major Component, but which is not part of\nthat Major Component, and (b) serves only to enable use of the work\nwith that Major Component, or to implement a Standard Interface for\nwhich an implementation is available to the public in source code\nform. A &ldquo;Major Component&rdquo;, in this context, means a major\nessential component (kernel, window system, and so on) of the\nspecific operating system (if any) on which the executable work runs,\nor a compiler used to produce the work, or an object code interpreter\nused to run it.</P>\n<P>The &ldquo;Corresponding Source&rdquo; for a work in object code\nform means all the source code needed to generate, install, and (for\nan executable work) run the object code and to modify the work,\nincluding scripts to control those activities. However, it does not\ninclude the work's System Libraries, or general-purpose tools or\ngenerally available free programs which are used unmodified in\nperforming those activities but which are not part of the work. For\nexample, Corresponding Source includes interface definition files\nassociated with source files for the work, and the source code for\nshared libraries and dynamically linked subprograms that the work is\nspecifically designed to require, such as by intimate data\ncommunication or control flow between those subprograms and other\nparts of the work.</P>\n<P>The Corresponding Source need not include anything that users can\nregenerate automatically from other parts of the Corresponding\nSource.</P>\n<P>The Corresponding Source for a work in source code form is that\nsame work.</P>\n<H4><A NAME=\"section2\"></A>2. Basic Permissions.</H4>\n<P>All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met. This License explicitly affirms your unlimited\npermission to run the unmodified Program. The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work. This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.</P>\n<P>You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force. You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply\nwith the terms of this License in conveying all material for which\nyou do not control copyright. Those thus making or running the\ncovered works for you must do so exclusively on your behalf, under\nyour direction and control, on terms that prohibit them from making\nany copies of your copyrighted material outside their relationship\nwith you.</P>\n<P>Conveying under any other circumstances is permitted solely under\nthe conditions stated below. Sublicensing is not allowed; section 10\nmakes it unnecessary.</P>\n<H4><A NAME=\"section3\"></A>3. Protecting Users' Legal Rights From\nAnti-Circumvention Law.</H4>\n<P>No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.</P>\n<P>When you convey a covered work, you waive any legal power to\nforbid circumvention of technological measures to the extent such\ncircumvention is effected by exercising rights under this License\nwith respect to the covered work, and you disclaim any intention to\nlimit operation or modification of the work as a means of enforcing,\nagainst the work's users, your or third parties' legal rights to\nforbid circumvention of technological measures.</P>\n<H4><A NAME=\"section4\"></A>4. Conveying Verbatim Copies.</H4>\n<P>You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the\ncode; keep intact all notices of the absence of any warranty; and\ngive all recipients a copy of this License along with the Program.</P>\n<P>You may charge any price or no price for each copy that you\nconvey, and you may offer support or warranty protection for a fee.</P>\n<H4><A NAME=\"section5\"></A>5. Conveying Modified Source Versions.</H4>\n<P>You may convey a work based on the Program, or the modifications\nto produce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these\nconditions:</P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0\">a) The work must carry prominent\n\tnotices stating that you modified it, and giving a relevant date. \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">b) The work must carry prominent\n\tnotices stating that it is released under this License and any\n\tconditions added under section 7. This requirement modifies the\n\trequirement in section 4 to &ldquo;keep intact all notices&rdquo;. \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">c) You must license the entire\n\twork, as a whole, under this License to anyone who comes into\n\tpossession of a copy. This License will therefore apply, along with\n\tany applicable section 7 additional terms, to the whole of the work,\n\tand all its parts, regardless of how they are packaged. This License\n\tgives no permission to license the work in any other way, but it\n\tdoes not invalidate such permission if you have separately received\n\tit. \n\t</P>\n\t<LI><P>d) If the work has interactive user interfaces, each must\n\tdisplay Appropriate Legal Notices; however, if the Program has\n\tinteractive interfaces that do not display Appropriate Legal\n\tNotices, your work need not make them do so. \n\t</P>\n</UL>\n<P>A compilation of a covered work with other separate and\nindependent works, which are not by their nature extensions of the\ncovered work, and which are not combined with it such as to form a\nlarger program, in or on a volume of a storage or distribution\nmedium, is called an &ldquo;aggregate&rdquo; if the compilation and\nits resulting copyright are not used to limit the access or legal\nrights of the compilation's users beyond what the individual works\npermit. Inclusion of a covered work in an aggregate does not cause\nthis License to apply to the other parts of the aggregate.</P>\n<H4><A NAME=\"section6\"></A>6. Conveying Non-Source Forms.</H4>\n<P>You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this\nLicense, in one of these ways:</P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0\">a) Convey the object code in, or\n\tembodied in, a physical product (including a physical distribution\n\tmedium), accompanied by the Corresponding Source fixed on a durable\n\tphysical medium customarily used for software interchange. \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">b) Convey the object code in, or\n\tembodied in, a physical product (including a physical distribution\n\tmedium), accompanied by a written offer, valid for at least three\n\tyears and valid for as long as you offer spare parts or customer\n\tsupport for that product model, to give anyone who possesses the\n\tobject code either (1) a copy of the Corresponding Source for all\n\tthe software in the product that is covered by this License, on a\n\tdurable physical medium customarily used for software interchange,\n\tfor a price no more than your reasonable cost of physically\n\tperforming this conveying of source, or (2) access to copy the\n\tCorresponding Source from a network server at no charge. \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">c) Convey individual copies of the\n\tobject code with a copy of the written offer to provide the\n\tCorresponding Source. This alternative is allowed only occasionally\n\tand noncommercially, and only if you received the object code with\n\tsuch an offer, in accord with subsection 6b. \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">d) Convey the object code by\n\toffering access from a designated place (gratis or for a charge),\n\tand offer equivalent access to the Corresponding Source in the same\n\tway through the same place at no further charge. You need not\n\trequire recipients to copy the Corresponding Source along with the\n\tobject code. If the place to copy the object code is a network\n\tserver, the Corresponding Source may be on a different server\n\t(operated by you or a third party) that supports equivalent copying\n\tfacilities, provided you maintain clear directions next to the\n\tobject code saying where to find the Corresponding Source.\n\tRegardless of what server hosts the Corresponding Source, you remain\n\tobligated to ensure that it is available for as long as needed to\n\tsatisfy these requirements. \n\t</P>\n\t<LI><P>e) Convey the object code using peer-to-peer transmission,\n\tprovided you inform other peers where the object code and\n\tCorresponding Source of the work are being offered to the general\n\tpublic at no charge under subsection 6d. \n\t</P>\n</UL>\n<P>A separable portion of the object code, whose source code is\nexcluded from the Corresponding Source as a System Library, need not\nbe included in conveying the object code work.</P>\n<P>A &ldquo;User Product&rdquo; is either (1) a &ldquo;consumer\nproduct&rdquo;, which means any tangible personal property which is\nnormally used for personal, family, or household purposes, or (2)\nanything designed or sold for incorporation into a dwelling. In\ndetermining whether a product is a consumer product, doubtful cases\nshall be resolved in favor of coverage. For a particular product\nreceived by a particular user, &ldquo;normally used&rdquo; refers to\na typical or common use of that class of product, regardless of the\nstatus of the particular user or of the way in which the particular\nuser actually uses, or expects or is expected to use, the product. A\nproduct is a consumer product regardless of whether the product has\nsubstantial commercial, industrial or non-consumer uses, unless such\nuses represent the only significant mode of use of the product.</P>\n<P>&ldquo;Installation Information&rdquo; for a User Product means\nany methods, procedures, authorization keys, or other information\nrequired to install and execute modified versions of a covered work\nin that User Product from a modified version of its Corresponding\nSource. The information must suffice to ensure that the continued\nfunctioning of the modified object code is in no case prevented or\ninterfered with solely because modification has been made.</P>\n<P>If you convey an object code work under this section in, or with,\nor specifically for use in, a User Product, and the conveying occurs\nas part of a transaction in which the right of possession and use of\nthe User Product is transferred to the recipient in perpetuity or for\na fixed term (regardless of how the transaction is characterized),\nthe Corresponding Source conveyed under this section must be\naccompanied by the Installation Information. But this requirement\ndoes not apply if neither you nor any third party retains the ability\nto install modified object code on the User Product (for example, the\nwork has been installed in ROM).</P>\n<P>The requirement to provide Installation Information does not\ninclude a requirement to continue to provide support service,\nwarranty, or updates for a work that has been modified or installed\nby the recipient, or for the User Product in which it has been\nmodified or installed. Access to a network may be denied when the\nmodification itself materially and adversely affects the operation of\nthe network or violates the rules and protocols for communication\nacross the network.</P>\n<P>Corresponding Source conveyed, and Installation Information\nprovided, in accord with this section must be in a format that is\npublicly documented (and with an implementation available to the\npublic in source code form), and must require no special password or\nkey for unpacking, reading or copying.</P>\n<H4><A NAME=\"section7\"></A>7. Additional Terms.</H4>\n<P>&ldquo;Additional permissions&rdquo; are terms that supplement the\nterms of this License by making exceptions from one or more of its\nconditions. Additional permissions that are applicable to the entire\nProgram shall be treated as though they were included in this\nLicense, to the extent that they are valid under applicable law. If\nadditional permissions apply only to part of the Program, that part\nmay be used separately under those permissions, but the entire\nProgram remains governed by this License without regard to the\nadditional permissions.</P>\n<P>When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit. (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.) You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.</P>\n<P>Notwithstanding any other provision of this License, for material\nyou add to a covered work, you may (if authorized by the copyright\nholders of that material) supplement the terms of this License with\nterms:</P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0\">a) Disclaiming warranty or\n\tlimiting liability differently from the terms of sections 15 and 16\n\tof this License; or \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">b) Requiring preservation of\n\tspecified reasonable legal notices or author attributions in that\n\tmaterial or in the Appropriate Legal Notices displayed by works\n\tcontaining it; or \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">c) Prohibiting misrepresentation\n\tof the origin of that material, or requiring that modified versions\n\tof such material be marked in reasonable ways as different from the\n\toriginal version; or \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">d) Limiting the use for publicity\n\tpurposes of names of licensors or authors of the material; or \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0\">e) Declining to grant rights under\n\ttrademark law for use of some trade names, trademarks, or service\n\tmarks; or \n\t</P>\n\t<LI><P>f) Requiring indemnification of licensors and authors of that\n\tmaterial by anyone who conveys the material (or modified versions of\n\tit) with contractual assumptions of liability to the recipient, for\n\tany liability that these contractual assumptions directly impose on\n\tthose licensors and authors. \n\t</P>\n</UL>\n<P>All other non-permissive additional terms are considered &ldquo;further\nrestrictions&rdquo; within the meaning of section 10. If the Program\nas you received it, or any part of it, contains a notice stating that\nit is governed by this License along with a term that is a further\nrestriction, you may remove that term. If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.</P>\n<P>If you add terms to a covered work in accord with this section,\nyou must place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.</P>\n<P>Additional terms, permissive or non-permissive, may be stated in\nthe form of a separately written license, or stated as exceptions;\nthe above requirements apply either way.</P>\n<H4><A NAME=\"section8\"></A>8. Termination.</H4>\n<P>You may not propagate or modify a covered work except as expressly\nprovided under this License. Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).</P>\n<P>However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the\ncopyright holder fails to notify you of the violation by some\nreasonable means prior to 60 days after the cessation.</P>\n<P>Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.</P>\n<P>Termination of your rights under this section does not terminate\nthe licenses of parties who have received copies or rights from you\nunder this License. If your rights have been terminated and not\npermanently reinstated, you do not qualify to receive new licenses\nfor the same material under section 10.</P>\n<H4><A NAME=\"section9\"></A>9. Acceptance Not Required for Having\nCopies.</H4>\n<P>You are not required to accept this License in order to receive or\nrun a copy of the Program. Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance. However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work. These actions infringe copyright if you do\nnot accept this License. Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.</P>\n<H4><A NAME=\"section10\"></A>10. Automatic Licensing of Downstream\nRecipients.</H4>\n<P>Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License. You are not responsible\nfor enforcing compliance by third parties with this License.</P>\n<P>An &ldquo;entity transaction&rdquo; is a transaction transferring\ncontrol of an organization, or substantially all assets of one, or\nsubdividing an organization, or merging organizations. If propagation\nof a covered work results from an entity transaction, each party to\nthat transaction who receives a copy of the work also receives\nwhatever licenses to the work the party's predecessor in interest had\nor could give under the previous paragraph, plus a right to\npossession of the Corresponding Source of the work from the\npredecessor in interest, if the predecessor has it or can get it with\nreasonable efforts.</P>\n<P>You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License. For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate\nlitigation (including a cross-claim or counterclaim in a lawsuit)\nalleging that any patent claim is infringed by making, using,\nselling, offering for sale, or importing the Program or any portion\nof it.</P>\n<H4><A NAME=\"section11\"></A>11. Patents.</H4>\n<P>A &ldquo;contributor&rdquo; is a copyright holder who authorizes\nuse under this License of the Program or a work on which the Program\nis based. The work thus licensed is called the contributor's\n&ldquo;contributor version&rdquo;.</P>\n<P>A contributor's &ldquo;essential patent claims&rdquo; are all\npatent claims owned or controlled by the contributor, whether already\nacquired or hereafter acquired, that would be infringed by some\nmanner, permitted by this License, of making, using, or selling its\ncontributor version, but do not include claims that would be\ninfringed only as a consequence of further modification of the\ncontributor version. For purposes of this definition, &ldquo;control&rdquo;\nincludes the right to grant patent sublicenses in a manner consistent\nwith the requirements of this License.</P>\n<P>Each contributor grants you a non-exclusive, worldwide,\nroyalty-free patent license under the contributor's essential patent\nclaims, to make, use, sell, offer for sale, import and otherwise run,\nmodify and propagate the contents of its contributor version.</P>\n<P>In the following three paragraphs, a &ldquo;patent license&rdquo;\nis any express agreement or commitment, however denominated, not to\nenforce a patent (such as an express permission to practice a patent\nor covenant not to sue for patent infringement). To &ldquo;grant&rdquo;\nsuch a patent license to a party means to make such an agreement or\ncommitment not to enforce a patent against the party.</P>\n<P>If you convey a covered work, knowingly relying on a patent\nlicense, and the Corresponding Source of the work is not available\nfor anyone to copy, free of charge and under the terms of this\nLicense, through a publicly available network server or other readily\naccessible means, then you must either (1) cause the Corresponding\nSource to be so available, or (2) arrange to deprive yourself of the\nbenefit of the patent license for this particular work, or (3)\narrange, in a manner consistent with the requirements of this\nLicense, to extend the patent license to downstream recipients.\n&ldquo;Knowingly relying&rdquo; means you have actual knowledge that,\nbut for the patent license, your conveying the covered work in a\ncountry, or your recipient's use of the covered work in a country,\nwould infringe one or more identifiable patents in that country that\nyou have reason to believe are valid.</P>\n<P>If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent\nlicense you grant is automatically extended to all recipients of the\ncovered work and works based on it.</P>\n<P>A patent license is &ldquo;discriminatory&rdquo; if it does not\ninclude within the scope of its coverage, prohibits the exercise of,\nor is conditioned on the non-exercise of one or more of the rights\nthat are specifically granted under this License. You may not convey\na covered work if you are a party to an arrangement with a third\nparty that is in the business of distributing software, under which\nyou make payment to the third party based on the extent of your\nactivity of conveying the work, and under which the third party\ngrants, to any of the parties who would receive the covered work from\nyou, a discriminatory patent license (a) in connection with copies of\nthe covered work conveyed by you (or copies made from those copies),\nor (b) primarily for and in connection with specific products or\ncompilations that contain the covered work, unless you entered into\nthat arrangement, or that patent license was granted, prior to 28\nMarch 2007.</P>\n<P>Nothing in this License shall be construed as excluding or\nlimiting any implied license or other defenses to infringement that\nmay otherwise be available to you under applicable patent law.</P>\n<H4><A NAME=\"section12\"></A>12. No Surrender of Others' Freedom.</H4>\n<P>If conditions are imposed on you (whether by court order,\nagreement or otherwise) that contradict the conditions of this\nLicense, they do not excuse you from the conditions of this License.\nIf you cannot convey a covered work so as to satisfy simultaneously\nyour obligations under this License and any other pertinent\nobligations, then as a consequence you may not convey it at all. For\nexample, if you agree to terms that obligate you to collect a royalty\nfor further conveying from those to whom you convey the Program, the\nonly way you could satisfy both those terms and this License would be\nto refrain entirely from conveying the Program.</P>\n<H4><A NAME=\"section13\"></A>13. Use with the GNU Affero General\nPublic License.</H4>\n<P>Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a\nsingle combined work, and to convey the resulting work. The terms of\nthis License will continue to apply to the part which is the covered\nwork, but the special requirements of the GNU Affero General Public\nLicense, section 13, concerning interaction through a network will\napply to the combination as such.</P>\n<H4><A NAME=\"section14\"></A>14. Revised Versions of this License.</H4>\n<P>The Free Software Foundation may publish revised and/or new\nversions of the GNU General Public License from time to time. Such\nnew versions will be similar in spirit to the present version, but\nmay differ in detail to address new problems or concerns.</P>\n<P>Each version is given a distinguishing version number. If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License &ldquo;or any later version&rdquo; applies to it, you\nhave the option of following the terms and conditions either of that\nnumbered version or of any later version published by the Free\nSoftware Foundation. If the Program does not specify a version number\nof the GNU General Public License, you may choose any version ever\npublished by the Free Software Foundation.</P>\n<P>If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes\nyou to choose that version for the Program.</P>\n<P>Later license versions may give you additional or different\npermissions. However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.</P>\n<H4><A NAME=\"section15\"></A>15. Disclaimer of Warranty.</H4>\n<P>THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM &ldquo;AS IS&rdquo;\nWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,\nBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY\nAND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE\nDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR\nCORRECTION.</P>\n<H4><A NAME=\"section16\"></A>16. Limitation of Liability.</H4>\n<P>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\nWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES\nAND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR\nDAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL\nDAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM\n(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED\nINACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE\nOF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH\nHOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES.</P>\n<H4><A NAME=\"section17\"></A>17. Interpretation of Sections 15 and 16.</H4>\n<P>If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.</P>\n<P>END OF TERMS AND CONDITIONS</P>\n<P><BR><BR>\n</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/lgpl.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<!-- saved from url=(0065)http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html -->\n<html lang=\"en\">\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <title>GNU Lesser General Public License v2.1 - GNU Project - Free Software Foundation (FSF)</title>\n    <link rel=\"alternate\" type=\"application/rdf+xml\" href=\"http://www.gnu.org/licenses/old-licenses/lgpl-2.1.rdf\">\n</head>\n<body>\n<h3><a id=\"SEC1\">GNU LESSER GENERAL PUBLIC LICENSE</a></h3>\n<p>\n    Version 2.1, February 1999\n</p>\n\n<pre>Copyright (C) 1991, 1999 Free Software Foundation, Inc.\n51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\n\n[This is the first released version of the Lesser GPL.  It also counts\n as the successor of the GNU Library Public License, version 2, hence\n the version number 2.1.]\n</pre>\n\n\n<h3><a id=\"SEC2\">Preamble</a></h3>\n\n<p>\n    The licenses for most software are designed to take away your\n    freedom to share and change it.  By contrast, the GNU General Public\n    Licenses are intended to guarantee your freedom to share and change\n    free software--to make sure the software is free for all its users.\n</p>\n<p>\n    This license, the Lesser General Public License, applies to some\n    specially designated software packages--typically libraries--of the\n    Free Software Foundation and other authors who decide to use it.  You\n    can use it too, but we suggest you first think carefully about whether\n    this license or the ordinary General Public License is the better\n    strategy to use in any particular case, based on the explanations below.\n</p>\n<p>\n    When we speak of free software, we are referring to freedom of use,\n    not price.  Our General Public Licenses are designed to make sure that\n    you have the freedom to distribute copies of free software (and charge\n    for this service if you wish); that you receive source code or can get\n    it if you want it; that you can change the software and use pieces of\n    it in new free programs; and that you are informed that you can do\n    these things.\n</p>\n<p>\n    To protect your rights, we need to make restrictions that forbid\n    distributors to deny you these rights or to ask you to surrender these\n    rights.  These restrictions translate to certain responsibilities for\n    you if you distribute copies of the library or if you modify it.\n</p>\n<p>\n    For example, if you distribute copies of the library, whether gratis\n    or for a fee, you must give the recipients all the rights that we gave\n    you.  You must make sure that they, too, receive or can get the source\n    code.  If you link other code with the library, you must provide\n    complete object files to the recipients, so that they can relink them\n    with the library after making changes to the library and recompiling\n    it.  And you must show them these terms so they know their rights.\n</p>\n<p>\n    We protect your rights with a two-step method: (1) we copyright the\n    library, and (2) we offer you this license, which gives you legal\n    permission to copy, distribute and/or modify the library.\n</p>\n<p>\n    To protect each distributor, we want to make it very clear that\n    there is no warranty for the free library.  Also, if the library is\n    modified by someone else and passed on, the recipients should know\n    that what they have is not the original version, so that the original\n    author's reputation will not be affected by problems that might be\n    introduced by others.\n</p>\n<p>\n    Finally, software patents pose a constant threat to the existence of\n    any free program.  We wish to make sure that a company cannot\n    effectively restrict the users of a free program by obtaining a\n    restrictive license from a patent holder.  Therefore, we insist that\n    any patent license obtained for a version of the library must be\n    consistent with the full freedom of use specified in this license.\n</p>\n<p>\n    Most GNU software, including some libraries, is covered by the\n    ordinary GNU General Public License.  This license, the GNU Lesser\n    General Public License, applies to certain designated libraries, and\n    is quite different from the ordinary General Public License.  We use\n    this license for certain libraries in order to permit linking those\n    libraries into non-free programs.\n</p>\n<p>\n    When a program is linked with a library, whether statically or using\n    a shared library, the combination of the two is legally speaking a\n    combined work, a derivative of the original library.  The ordinary\n    General Public License therefore permits such linking only if the\n    entire combination fits its criteria of freedom.  The Lesser General\n    Public License permits more lax criteria for linking other code with\n    the library.\n</p>\n<p>\n    We call this license the \"Lesser\" General Public License because it\n    does Less to protect the user's freedom than the ordinary General\n    Public License.  It also provides other free software developers Less\n    of an advantage over competing non-free programs.  These disadvantages\n    are the reason we use the ordinary General Public License for many\n    libraries.  However, the Lesser license provides advantages in certain\n    special circumstances.\n</p>\n<p>\n    For example, on rare occasions, there may be a special need to\n    encourage the widest possible use of a certain library, so that it becomes\n    a de-facto standard.  To achieve this, non-free programs must be\n    allowed to use the library.  A more frequent case is that a free\n    library does the same job as widely used non-free libraries.  In this\n    case, there is little to gain by limiting the free library to free\n    software only, so we use the Lesser General Public License.\n</p>\n<p>\n    In other cases, permission to use a particular library in non-free\n    programs enables a greater number of people to use a large body of\n    free software.  For example, permission to use the GNU C Library in\n    non-free programs enables many more people to use the whole GNU\n    operating system, as well as its variant, the GNU/Linux operating\n    system.\n</p>\n<p>\n    Although the Lesser General Public License is Less protective of the\n    users' freedom, it does ensure that the user of a program that is\n    linked with the Library has the freedom and the wherewithal to run\n    that program using a modified version of the Library.\n</p>\n<p>\n    The precise terms and conditions for copying, distribution and\n    modification follow.  Pay close attention to the difference between a\n    \"work based on the library\" and a \"work that uses the library\".  The\n    former contains code derived from the library, whereas the latter must\n    be combined with the library in order to run.\n</p>\n\n<h3><a id=\"SEC3\">TERMS AND CONDITIONS FOR COPYING,\n    DISTRIBUTION AND MODIFICATION</a></h3>\n\n\n<p>\n    <strong>0.</strong>\n    This License Agreement applies to any software library or other\n    program which contains a notice placed by the copyright holder or\n    other authorized party saying it may be distributed under the terms of\n    this Lesser General Public License (also called \"this License\").\n    Each licensee is addressed as \"you\".\n</p>\n<p>\n    A \"library\" means a collection of software functions and/or data\n    prepared so as to be conveniently linked with application programs\n    (which use some of those functions and data) to form executables.\n</p>\n<p>\n    The \"Library\", below, refers to any such software library or work\n    which has been distributed under these terms.  A \"work based on the\n    Library\" means either the Library or any derivative work under\n    copyright law: that is to say, a work containing the Library or a\n    portion of it, either verbatim or with modifications and/or translated\n    straightforwardly into another language.  (Hereinafter, translation is\n    included without limitation in the term \"modification\".)\n</p>\n<p>\n    \"Source code\" for a work means the preferred form of the work for\n    making modifications to it.  For a library, complete source code means\n    all the source code for all modules it contains, plus any associated\n    interface definition files, plus the scripts used to control compilation\n    and installation of the library.\n</p>\n<p>\n    Activities other than copying, distribution and modification are not\n    covered by this License; they are outside its scope.  The act of\n    running a program using the Library is not restricted, and output from\n    such a program is covered only if its contents constitute a work based\n    on the Library (independent of the use of the Library in a tool for\n    writing it).  Whether that is true depends on what the Library does\n    and what the program that uses the Library does.\n</p>\n<p>\n    <strong>1.</strong>\n    You may copy and distribute verbatim copies of the Library's\n    complete source code as you receive it, in any medium, provided that\n    you conspicuously and appropriately publish on each copy an\n    appropriate copyright notice and disclaimer of warranty; keep intact\n    all the notices that refer to this License and to the absence of any\n    warranty; and distribute a copy of this License along with the\n    Library.\n</p>\n<p>\n    You may charge a fee for the physical act of transferring a copy,\n    and you may at your option offer warranty protection in exchange for a\n    fee.\n</p>\n<p>\n    <strong>2.</strong>\n    You may modify your copy or copies of the Library or any portion\n    of it, thus forming a work based on the Library, and copy and\n    distribute such modifications or work under the terms of Section 1\n    above, provided that you also meet all of these conditions:\n</p>\n\n<ul>\n    <li><strong>a)</strong>\n        The modified work must itself be a software library.</li>\n    <li><strong>b)</strong>\n        You must cause the files modified to carry prominent notices\n        stating that you changed the files and the date of any change.</li>\n\n    <li><strong>c)</strong>\n        You must cause the whole of the work to be licensed at no\n        charge to all third parties under the terms of this License.</li>\n\n    <li><strong>d)</strong>\n        If a facility in the modified Library refers to a function or a\n        table of data to be supplied by an application program that uses\n        the facility, other than as an argument passed when the facility\n        is invoked, then you must make a good faith effort to ensure that,\n        in the event an application does not supply such function or\n        table, the facility still operates, and performs whatever part of\n        its purpose remains meaningful.\n        <p>\n            (For example, a function in a library to compute square roots has\n            a purpose that is entirely well-defined independent of the\n            application.  Therefore, Subsection 2d requires that any\n            application-supplied function or table used by this function must\n            be optional: if the application does not supply it, the square\n            root function must still compute square roots.)</p></li>\n</ul>\n\n<p>\n    These requirements apply to the modified work as a whole.  If identifiable\n    sections of that work are not derived from the Library, and can be\n    reasonably considered independent and separate works in themselves, then\n    this License, and its terms, do not apply to those sections when you\n    distribute them as separate works.  But when you distribute the same\n    sections as part of a whole which is a work based on the Library, the\n    distribution of the whole must be on the terms of this License, whose\n    permissions for other licensees extend to the entire whole, and thus to\n    each and every part regardless of who wrote it.\n</p>\n<p>\n    Thus, it is not the intent of this section to claim rights or contest your\n    rights to work written entirely by you; rather, the intent is to exercise\n    the right to control the distribution of derivative or collective works\n    based on the Library.\n</p>\n<p>\n    In addition, mere aggregation of another work not based on the Library with\n    the Library (or with a work based on the Library) on a volume of a storage\n    or distribution medium does not bring the other work under the scope of\n    this License.\n</p>\n<p>\n    <strong>3.</strong>\n    You may opt to apply the terms of the ordinary GNU General Public\n    License instead of this License to a given copy of the Library.  To do\n    this, you must alter all the notices that refer to this License, so\n    that they refer to the ordinary GNU General Public License, version 2,\n    instead of to this License.  (If a newer version than version 2 of the\n    ordinary GNU General Public License has appeared, then you can specify\n    that version instead if you wish.)  Do not make any other change in\n    these notices.\n</p>\n<p>\n    Once this change is made in a given copy, it is irreversible for\n    that copy, so the ordinary GNU General Public License applies to all\n    subsequent copies and derivative works made from that copy.\n</p>\n<p>\n    This option is useful when you wish to copy part of the code of\n    the Library into a program that is not a library.\n</p>\n<p>\n    <strong>4.</strong>\n    You may copy and distribute the Library (or a portion or\n    derivative of it, under Section 2) in object code or executable form\n    under the terms of Sections 1 and 2 above provided that you accompany\n    it with the complete corresponding machine-readable source code, which\n    must be distributed under the terms of Sections 1 and 2 above on a\n    medium customarily used for software interchange.\n</p>\n<p>\n    If distribution of object code is made by offering access to copy\n    from a designated place, then offering equivalent access to copy the\n    source code from the same place satisfies the requirement to\n    distribute the source code, even though third parties are not\n    compelled to copy the source along with the object code.\n</p>\n<p>\n    <strong>5.</strong>\n    A program that contains no derivative of any portion of the\n    Library, but is designed to work with the Library by being compiled or\n    linked with it, is called a \"work that uses the Library\".  Such a\n    work, in isolation, is not a derivative work of the Library, and\n    therefore falls outside the scope of this License.\n</p>\n<p>\n    However, linking a \"work that uses the Library\" with the Library\n    creates an executable that is a derivative of the Library (because it\n    contains portions of the Library), rather than a \"work that uses the\n    library\".  The executable is therefore covered by this License.\n    Section 6 states terms for distribution of such executables.\n</p>\n<p>\n    When a \"work that uses the Library\" uses material from a header file\n    that is part of the Library, the object code for the work may be a\n    derivative work of the Library even though the source code is not.\n    Whether this is true is especially significant if the work can be\n    linked without the Library, or if the work is itself a library.  The\n    threshold for this to be true is not precisely defined by law.\n</p>\n<p>\n    If such an object file uses only numerical parameters, data\n    structure layouts and accessors, and small macros and small inline\n    functions (ten lines or less in length), then the use of the object\n    file is unrestricted, regardless of whether it is legally a derivative\n    work.  (Executables containing this object code plus portions of the\n    Library will still fall under Section 6.)\n</p>\n<p>\n    Otherwise, if the work is a derivative of the Library, you may\n    distribute the object code for the work under the terms of Section 6.\n    Any executables containing that work also fall under Section 6,\n    whether or not they are linked directly with the Library itself.\n</p>\n<p>\n    <strong>6.</strong>\n    As an exception to the Sections above, you may also combine or\n    link a \"work that uses the Library\" with the Library to produce a\n    work containing portions of the Library, and distribute that work\n    under terms of your choice, provided that the terms permit\n    modification of the work for the customer's own use and reverse\n    engineering for debugging such modifications.\n</p>\n<p>\n    You must give prominent notice with each copy of the work that the\n    Library is used in it and that the Library and its use are covered by\n    this License.  You must supply a copy of this License.  If the work\n    during execution displays copyright notices, you must include the\n    copyright notice for the Library among them, as well as a reference\n    directing the user to the copy of this License.  Also, you must do one\n    of these things:\n</p>\n\n<ul>\n    <li><strong>a)</strong> Accompany the work with the complete\n        corresponding machine-readable source code for the Library\n        including whatever changes were used in the work (which must be\n        distributed under Sections 1 and 2 above); and, if the work is an\n        executable linked with the Library, with the complete\n        machine-readable \"work that uses the Library\", as object code\n        and/or source code, so that the user can modify the Library and\n        then relink to produce a modified executable containing the\n        modified Library.  (It is understood that the user who changes the\n        contents of definitions files in the Library will not necessarily\n        be able to recompile the application to use the modified\n        definitions.)</li>\n\n    <li><strong>b)</strong> Use a suitable shared library mechanism\n        for linking with the Library.  A suitable mechanism is one that\n        (1) uses at run time a copy of the library already present on the\n        user's computer system, rather than copying library functions into\n        the executable, and (2) will operate properly with a modified\n        version of the library, if the user installs one, as long as the\n        modified version is interface-compatible with the version that the\n        work was made with.</li>\n\n    <li><strong>c)</strong> Accompany the work with a written offer,\n        valid for at least three years, to give the same user the\n        materials specified in Subsection 6a, above, for a charge no more\n        than the cost of performing this distribution.</li>\n\n    <li><strong>d)</strong> If distribution of the work is made by\n        offering access to copy from a designated place, offer equivalent\n        access to copy the above specified materials from the same\n        place.</li>\n\n    <li><strong>e)</strong> Verify that the user has already received\n        a copy of these materials or that you have already sent this user\n        a copy.</li>\n</ul>\n\n<p>\n    For an executable, the required form of the \"work that uses the\n    Library\" must include any data and utility programs needed for\n    reproducing the executable from it.  However, as a special exception,\n    the materials to be distributed need not include anything that is\n    normally distributed (in either source or binary form) with the major\n    components (compiler, kernel, and so on) of the operating system on\n    which the executable runs, unless that component itself accompanies\n    the executable.\n</p>\n<p>\n    It may happen that this requirement contradicts the license\n    restrictions of other proprietary libraries that do not normally\n    accompany the operating system.  Such a contradiction means you cannot\n    use both them and the Library together in an executable that you\n    distribute.\n</p>\n<p>\n    <strong>7.</strong> You may place library facilities that are a work\n    based on the Library side-by-side in a single library together with\n    other library facilities not covered by this License, and distribute\n    such a combined library, provided that the separate distribution of\n    the work based on the Library and of the other library facilities is\n    otherwise permitted, and provided that you do these two things:\n</p>\n\n<ul>\n    <li><strong>a)</strong> Accompany the combined library with a copy\n        of the same work based on the Library, uncombined with any other\n        library facilities.  This must be distributed under the terms of\n        the Sections above.</li>\n\n    <li><strong>b)</strong> Give prominent notice with the combined\n        library of the fact that part of it is a work based on the\n        Library, and explaining where to find the accompanying uncombined\n        form of the same work.</li>\n</ul>\n\n<p>\n    <strong>8.</strong> You may not copy, modify, sublicense, link with,\n    or distribute the Library except as expressly provided under this\n    License.  Any attempt otherwise to copy, modify, sublicense, link\n    with, or distribute the Library is void, and will automatically\n    terminate your rights under this License.  However, parties who have\n    received copies, or rights, from you under this License will not have\n    their licenses terminated so long as such parties remain in full\n    compliance.\n</p>\n<p>\n    <strong>9.</strong>\n    You are not required to accept this License, since you have not\n    signed it.  However, nothing else grants you permission to modify or\n    distribute the Library or its derivative works.  These actions are\n    prohibited by law if you do not accept this License.  Therefore, by\n    modifying or distributing the Library (or any work based on the\n    Library), you indicate your acceptance of this License to do so, and\n    all its terms and conditions for copying, distributing or modifying\n    the Library or works based on it.\n</p>\n<p>\n    <strong>10.</strong>\n    Each time you redistribute the Library (or any work based on the\n    Library), the recipient automatically receives a license from the\n    original licensor to copy, distribute, link with or modify the Library\n    subject to these terms and conditions.  You may not impose any further\n    restrictions on the recipients' exercise of the rights granted herein.\n    You are not responsible for enforcing compliance by third parties with\n    this License.\n</p>\n<p>\n    <strong>11.</strong>\n    If, as a consequence of a court judgment or allegation of patent\n    infringement or for any other reason (not limited to patent issues),\n    conditions are imposed on you (whether by court order, agreement or\n    otherwise) that contradict the conditions of this License, they do not\n    excuse you from the conditions of this License.  If you cannot\n    distribute so as to satisfy simultaneously your obligations under this\n    License and any other pertinent obligations, then as a consequence you\n    may not distribute the Library at all.  For example, if a patent\n    license would not permit royalty-free redistribution of the Library by\n    all those who receive copies directly or indirectly through you, then\n    the only way you could satisfy both it and this License would be to\n    refrain entirely from distribution of the Library.\n</p>\n<p>\n    If any portion of this section is held invalid or unenforceable under any\n    particular circumstance, the balance of the section is intended to apply,\n    and the section as a whole is intended to apply in other circumstances.\n</p>\n<p>\n    It is not the purpose of this section to induce you to infringe any\n    patents or other property right claims or to contest validity of any\n    such claims; this section has the sole purpose of protecting the\n    integrity of the free software distribution system which is\n    implemented by public license practices.  Many people have made\n    generous contributions to the wide range of software distributed\n    through that system in reliance on consistent application of that\n    system; it is up to the author/donor to decide if he or she is willing\n    to distribute software through any other system and a licensee cannot\n    impose that choice.\n</p>\n<p>\n    This section is intended to make thoroughly clear what is believed to\n    be a consequence of the rest of this License.\n</p>\n<p>\n    <strong>12.</strong>\n    If the distribution and/or use of the Library is restricted in\n    certain countries either by patents or by copyrighted interfaces, the\n    original copyright holder who places the Library under this License may add\n    an explicit geographical distribution limitation excluding those countries,\n    so that distribution is permitted only in or among countries not thus\n    excluded.  In such case, this License incorporates the limitation as if\n    written in the body of this License.\n</p>\n<p>\n    <strong>13.</strong>\n    The Free Software Foundation may publish revised and/or new\n    versions of the Lesser General Public License from time to time.\n    Such new versions will be similar in spirit to the present version,\n    but may differ in detail to address new problems or concerns.\n</p>\n<p>\n    Each version is given a distinguishing version number.  If the Library\n    specifies a version number of this License which applies to it and\n    \"any later version\", you have the option of following the terms and\n    conditions either of that version or of any later version published by\n    the Free Software Foundation.  If the Library does not specify a\n    license version number, you may choose any version ever published by\n    the Free Software Foundation.\n</p>\n<p>\n    <strong>14.</strong>\n    If you wish to incorporate parts of the Library into other free\n    programs whose distribution conditions are incompatible with these,\n    write to the author to ask for permission.  For software which is\n    copyrighted by the Free Software Foundation, write to the Free\n    Software Foundation; we sometimes make exceptions for this.  Our\n    decision will be guided by the two goals of preserving the free status\n    of all derivatives of our free software and of promoting the sharing\n    and reuse of software generally.\n</p>\n<p>\n    <strong>NO WARRANTY</strong>\n</p>\n<p>\n    <strong>15.</strong>\n    BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO\n    WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\n    EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR\n    OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY\n    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE\n    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n    PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\n    LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME\n    THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n</p>\n<p>\n    <strong>16.</strong>\n    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\n    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\n    AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU\n    FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR\n    CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE\n    LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING\n    RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\n    FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF\n    SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\n    DAMAGES.\n</p>\n\n<h3>END OF TERMS AND CONDITIONS</h3>\n\n</body></html>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/notice.html",
    "content": "<html lang=\"en\">\n<head>\n    <title></title>\n    <style>\n        table, th, td {\n            border: 1px solid black;\n        }\n\n        th, td {\n            padding: 10px;\n        }\n\n        body {\n            color: black;\n            background-color: white;\n        }\n    </style>\n</head>\n<body>\n<H1>jGnash ${version}</H1>\n<b>Build Date: </b> ${timeStamp}<br>\n<b>Build JVM: </b> ${javaVersion}<p>\n<b>Copyright (C) 2001-2021 Craig Cavanaugh</b>\n<p>jGnash comes with <STRONG>ABSOLUTELY NO WARRANTY</STRONG>.<p>\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n<p>\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n<p>\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see http://www.gnu.org/licenses/\n</body>\n</html>\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/html/en/xstream-license.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML lang=\"en\">\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=iso-8859-1\">\n\t<TITLE></TITLE>\t\n</HEAD>\n<BODY LANG=\"en-US\" DIR=\"LTR\">\n<h2>BSD License for XStream</h2>\n\n<PRE>Copyright (c) 2003-2006, Joe Walnes\nCopyright (c) 2006-2015, XStream Committers\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of\nconditions and the following disclaimer. Redistributions in binary form must reproduce\nthe above copyright notice, this list of conditions and the following disclaimer in\nthe documentation and/or other materials provided with the distribution.\n\nNeither the name of XStream nor the names of its contributors may be used to endorse\nor promote products derived from this software without specific prior written\npermission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT\nSHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY\nWAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.</PRE><P>\n<BR><BR>\n</P>\n</BODY>\n</HTML>"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource.properties",
    "content": "\nAccountType.Asset            = Asset\nAccountType.Bank             = Bank\nAccountType.Cash             = Cash\nAccountType.Checking         = Checking\nAccountType.Credit           = Credit\nAccountType.Equity           = Equity\nAccountType.Expense          = Expense\nAccountType.Income           = Income\nAccountType.Investment       = Investment\nAccountType.Liability        = Liability\nAccountType.MoneyMarket      = Money Market\nAccountType.Mutual           = Mutual Fund\nAccountType.Root             = Root\nAccountType.SimpleInvestment = Simple Investment\n\nButton.AccTerms                = Use formal accounting terms\nButton.Accounts                = Accounts\nButton.AckSel                  = Acknowledge Selected\nButton.Add                     = Add\nButton.AllDates                = All Dates\nButton.Amortize                = Amortize\nButton.AnimationsEnabled       = Enable animation effects\nButton.AnyStatus               = Any Status\nButton.Apply                   = Apply\nButton.AssetAccounts           = Asset Accounts\nButton.AutoReconcile           = Automatically Reconcile Split Transactions\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = Back\nButton.BankAccounts            = Bank Accounts\nButton.BudgetMgr               = Budget Manager\nButton.Budgeting               = Budgeting\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Cancel\nButton.CheckForUpdates         = Check for new updates\nButton.CheckReminders          = Check Reminders\nButton.Clear                   = Clear\nButton.ClearAll                = Clear All\nButton.Cleared                 = Cleared\nButton.Close                   = Close\nButton.Compare                 = Compare\nButton.ConcatenateMemos        = Concatenate Memos\nButton.ConfirmReminderDelete   = Confirm on reminder delete\nButton.ConfirmTransDelete      = Confirm on transaction delete\nButton.CopyToClip              = Copy To Clipboard\nButton.CreateTimeFile          = Create timestamped file on exit\nButton.CreditAccounts          = Credit Accounts\nButton.Delete                  = Delete\nButton.DeleteAll               = Delete All\nButton.DeleteWeekends          = Delete Weekends\nButton.DetailSplits            = Show Split Details\nButton.Duplicate               = Duplicate\nButton.Edit                    = Edit\nButton.EnableAutoComplete      = Enable auto completion\nButton.Enabled                 = Enabled\nButton.EndingBalance           = Ending Balance\nButton.Enter                   = Enter\nButton.EnterDaysBefore         = Automatically enter number of days before\nButton.ExcludeFromBudget       = Exclude From Budgets\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Expense Accounts\nButton.Export                  = Export\nButton.ExportSpreadsheet       = Export Spreadsheet\nButton.Filter                  = Filter\nButton.Finish                  = Finish\nButton.FinishLater             = Finish Later\nButton.ForceDefaultCurrency    = Force use of Default Currency\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = Requires Authentication\nButton.Hidden                  = Hidden\nButton.HideAccount             = Hide Account\nButton.HideLockedAccount       = Hide locked accounts\nButton.HidePlaceholderAccount  = Hide placeholder accounts\nButton.HideZeroBalance         = Hide zero balance accounts\nButton.HistoricalFill          = Historical Fill\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Include Sub Accounts\nButton.IncomeAccounts          = Income Accounts\nButton.IncomeAndExpense        = Income and Expense\nButton.Insert                  = Insert\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Invert Selection\nButton.Jump                    = Jump\nButton.KeepFridays             = Keep Fridays\nButton.KeepMondays             = Keep Mondays\nButton.Landscape               = Landscape\nButton.Last120Days             = Last 120 Days\nButton.Last12Months            = Last 12 Months\nButton.Last30Days              = Last 30 Days\nButton.Last60Days              = Last 60 Days\nButton.Last90Days              = Last 90 Days\nButton.LiabilityAccounts       = Liability Accounts\nButton.Locked                  = Locked\nButton.MatchAccountOnly        = Match using only account specific transactions\nButton.MatchAllTrans           = Match using all transactions\nButton.MatchCaseSensitive      = Match is case sensitive\nButton.Modify                  = Modify\nButton.MonthlyBalance          = Monthly Balance\nButton.New                     = New\nButton.NewEmpty                = New Empty\nButton.NewHist                 = New Historical\nButton.NewPayment              = New Payment\nButton.Next                    = Next\nButton.No                      = No\nButton.NoEndDate               = No End Date\nButton.None                    = None\nButton.NotReconciled           = Not Reconciled\nButton.Ok                      = OK\nButton.Open                    = Open\nButton.OpenLastOnStartup       = Open last file on startup\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Page Setup\nButton.PlaceHolder             = Placeholder\nButton.Portrait                = Portrait\nButton.Print                   = Print\nButton.PrintSample             = Print sample\nButton.Properties              = Properties\nButton.Reconcile               = Reconcile\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disable automatic reconciliation\nButton.ReconcileIncomeExpense  = Automatically reconcile Income and Expense Accounts\nButton.Reconciled              = Reconciled\nButton.Refresh                 = Refresh\nButton.RegDate                 = Remember last transaction date\nButton.Register                = Register\nButton.RegisterFollowsList     = Register follows account list\nButton.RememberPassword        = Remember Password\nButton.RemindLater             = Remind Me Later\nButton.Reminders               = Reminders\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Remove\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Resize\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Running Balance\nButton.Save                    = Save\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Select\nButton.SelectAll               = Select All\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Show all Commodities\nButton.ShowEmptyAccounts       = Show Zero Balance Accounts\nButton.ShowPercentValues       = Show Percentages\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Splits\nButton.Start                   = Start\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Themes Enabled\nButton.Timestamp               = Timestamp\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Update currency exchange rates on startup\nButton.UpdateOnline            = Update Online\nButton.UpdateSecuritiesStartup = Update securities on startup\nButton.UseDailyRate            = Use Daily Periodic Rate\nButton.UseEncryption           = Use Encryption\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Use Long Names\nButton.UseProxy                = Use Proxy\nButton.UseRegexForFilter       = Use regular expressions for filters\nButton.Vertical                = Vertical\nButton.Yes                     = Yes\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Account\nColumn.AccountName                    = Account Name\nColumn.Action                         = Action\nColumn.Actual                         = Actual\nColumn.Amount                         = Amount\nColumn.Approve                        = Approve\nColumn.Balance                        = Balance\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Charge\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = Commodity\nColumn.CostBasis                      = CB\nColumn.Credit                         = Credit\nColumn.Currency                       = Currency\nColumn.Date                           = Date\nColumn.Day                            = Day\nColumn.Debit                          = Debit\nColumn.Decrease                       = Decrease\nColumn.Deposit                        = Deposit\nColumn.Description                    = Description\nColumn.Due                            = Due\nColumn.Enabled                        = Enabled\nColumn.Entries                        = Entries\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Exchange Rate\nColumn.Expense                        = Expense\nColumn.Freq                           = Frequency\nColumn.Gain                           = Gain\nColumn.High                           = High\nColumn.Income                         = Income\nColumn.Increase                       = Increase\nColumn.Investment                     = Investment\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Loss\nColumn.Low                            = Low\nColumn.Memo                           = Memo\nColumn.MktValue                       = Mkt Value\nColumn.Month                          = Month\nColumn.Num                            = Num\nColumn.Payee                          = Payee\nColumn.Payment                        = Payment\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Price\nColumn.Print                          = Print\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Quantity\nColumn.Rebate                         = Rebate\nColumn.Receive                        = Receive\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = Spend\nColumn.Timestamp                      = Timestamp\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Type\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Withdrawal\n\nDataStoreType.Bxds = Binary File\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Amount\nItem.CashDeposit    = Cash Deposit\nItem.CashWithdrawal = Cash Withdrawal\nItem.Date           = Date\nItem.EFT            = EFT\nItem.Memo           = Memo\nItem.NextNum        = Next #\nItem.Payee          = Payee\nItem.Trans          = Trans\n\nLabel.AccentColor         = Accent Color:\nLabel.Account             = Account:\nLabel.AccountCode         = Account Code:\nLabel.AccountNumber       = Account Number:\nLabel.AccountOptions      = Account Options:\nLabel.AccountSeparator    = Account Separator:\nLabel.AccountStatus       = Account Status:\nLabel.AccountType         = Account Type:\nLabel.Action              = Action:\nLabel.Amount              = Amount:\nLabel.AnIntRate           = Annual Interest Rate (APR):\nLabel.Available           = Available:\nLabel.Balance             = Balance:\nLabel.BankAccount         = Bank Account:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Base Account:\nLabel.BaseColor           = Base Color:\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = Cash Balance:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Commodity:\nLabel.CompDaysPerYear     = Compounding Days per Year:\nLabel.CompPerTerm         = Compounding Periods per Year:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Count:\nLabel.CreateCurr          = Create Custom Currency:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Currency / Commodity:\nLabel.Currencies          = Currencies:\nLabel.Currency            = Currency:\nLabel.Current             = Current:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Date:\nLabel.DateFormat          = Date Format:\nLabel.DaysPastDue         = Days past due:\nLabel.DefaultCurrency     = Default Currency:\nLabel.DelaySec            = Delay (seconds)\nLabel.Description         = Description:\nLabel.DestAccount         = Destination Account:\nLabel.Difference          = Difference:\nLabel.Dividend            = Dividend:\nLabel.EndDate             = End Date:\nLabel.EndOn               = End On:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Ending Balance:\nLabel.EquityAccount       = Equity Account:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Every:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Exchange Rate:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Fees:\nLabel.FeesAccount         = Fees Account:\nLabel.FileName            = File Name:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Date of first payment:\nLabel.FocusColor          = Focus Color:\nLabel.Format              = Format:\nLabel.Frequency           = Frequency:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gains:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Height:\nLabel.High                = High:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = CUSIP / ISIN:\nLabel.IncomeAccount       = Income Account:\nLabel.InterestAccount     = Interest Account:\nLabel.LastOccurrence      = Last Occurrence:\nLabel.Layout              = Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = Length of Loan (Months):\nLabel.Low                 = Low:\nLabel.MarketValue         = Market Value:\nLabel.MaxBackupCount      = Maximum number of backup files to keep:\nLabel.Memo                = Memo:\nLabel.Monospace           = Monospace:\nLabel.Name                = Name:\nLabel.NetIncome           = Net Income:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Next payment date:\nLabel.Notes               = Notes:\nLabel.NumTrans            = Number of Transactions:\nLabel.Number              = Number:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date:\nLabel.OpeningBalance      = Opening Balance:\nLabel.OrigLoanAmt         = Original Loan Amount:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Password:\nLabel.Path                = Path\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Payments per Year:\nLabel.Payee               = Payee:\nLabel.Period              = Period:\nLabel.Port                = Port:\nLabel.Prefix              = Prefix:\nLabel.Price               = Price:\nLabel.Proportional        = Proportional:\nLabel.Quantity            = Quantity:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Reconciled Balance:\nLabel.RemindLater         = Remind me again after:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Repeat on:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = Resolution:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Scale:\nLabel.Securities          = Securities:\nLabel.Security            = Security:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Show Empty Accounts\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Start Date:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Start Position:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Statement Date:\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = Suffix:\nLabel.Symbol              = Symbol:\nLabel.TargetBalance       = Target Balance:\nLabel.Top                 = Top:\nLabel.Total               = Total:\nLabel.Transaction         = Transaction:\nLabel.TransferFrom        = Transfer From:\nLabel.TransferTo          = Transfer To:\nLabel.Type                = Type:\nLabel.Units               = Units:\nLabel.UserName            = User Name:\nLabel.Value               = Value:\nLabel.Verify              = Verify:\nLabel.VerifyPassword      = Verify Password:\nLabel.Visible             = Visible:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = _About\\u2026\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = Account\nMenu.AccountRegister.Name             = Account Register\\u2026\nMenu.AddRemoveCurrency.Name           = _Add / Remove\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Update Currencies\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = Update Securities\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet\\u2026\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = _Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Cleared\nMenu.Close.Name                       = Close\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters\\u2026\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = C_opy\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = C_urrencies\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Change _default\\u2026\nMenu.DefaultCurrency.Tooltip          = Change the default currency\nMenu.Delete.Name                      = Delete\nMenu.Duplicate.Name                   = Duplicate\nMenu.Edit.Name                        = _Edit\nMenu.EditTranNumList.Name             = Edit Transaction Number List\\u2026\nMenu.Exit.Name                        = _Quit\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = _Export\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archive\\u2026\nMenu.File.Name                        = _File\nMenu.Filter.Name                      = _Filters\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Help\nMenu.Hide.Name                        = Hide\nMenu.HistoryChart.Name                = Historical Chart\\u2026\nMenu.HistoryCommodity.Name            = _History\\u2026\nMenu.HistoryImport.Name               = Historical Import\\u2026\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Income / Expense Pie Chart\\u2026\nMenu.Import.Name                      = _Import\nMenu.ImportAccounts.Name              = Import Accounts\\u2026\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Import jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = List of Accounts\\u2026\nMenu.Locale.Name                      = Change Locale\\u2026\nMenu.LookAndFeel.Name                 = Look and Feel\nMenu.MarkAs.Name                      = Mark As\nMenu.Modify.Name                      = Modify\nMenu.ModifyCommodity.Name             = _Create / Modify\\u2026\nMenu.ModifyCurrency.Name              = _Modify\\u2026\nMenu.ModifyExchangeRates.Name         = _Edit Exchange Rates\\u2026\nMenu.MonthBalance.Name                = Monthly Balance\\u2026\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = End-Of-Month Balance\\u2026\nMenu.MonthEndBalanceCSV.Name          = End-Of-Month Balance (CSV)\\u2026\nMenu.NetWorth.Name                    = Net Worth\\u2026\nMenu.New.Name                         = _New\\u2026\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Open\\u2026\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = _Options\\u2026\nMenu.Option.Tooltip                   = Change program options\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = Periodic Account Balance\\u2026\nMenu.Portfolio.Name                   = Portfolio\\u2026\nMenu.ProfitLoss.Name                  = Profit and Loss\\u2026\nMenu.ProfitLossTXT.Name               = Profit and Loss (Text)\\u2026\nMenu.Reconcile.Name                   = Reconcile\nMenu.Reconciled.Name                  = Reconciled\nMenu.RecurringList.Name               = Recurring Transactions\\u2026\nMenu.Register.Name                    = _Register\\u2026\nMenu.Reports.Name                     = _Reports\nMenu.RunJavaScript.Name               = Run JavaScript\\u2026\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = _Save\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = Save _As\\u2026\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = _Securities\nMenu.SetPassword.Name                 = Set Password\\u2026\nMenu.Show.Name                        = Show\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name                  = Tag Manager\\u2026\nMenu.Themes.Name                      = Themes\nMenu.Tools.Name                       = _Tools\nMenu.TransactionTagPieChart.Name      = Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Unreconciled\nMenu.View.Name                        = _View\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = I have read, understand, and accept the terms of the licenses.\nMessage.AccountAdd                   = Account added\nMessage.AccountCode                  = Account code was not unique: The code was not changed\nMessage.AccountLocked                = Account is Locked\nMessage.AccountModify                = Account modified\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = Account removed\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = AutoSave has been turned off\nMessage.AutoSaveOn                   = AutoSave has been turned on\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Checking for new recurring events\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\\u2026\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Delete the selected transactions?\nMessage.ConfirmReminderDelete        = Delete the selected reminder?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Delete the selected transaction?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Default currency changed to:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine started\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Unable to create account\nMessage.Error.AccountRemove          = Could not remove account\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Could not add commodity\nMessage.Error.AddCurrency            = Could not add currency\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Create a basic account set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = File was not found\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = The license was not accepted\nMessage.Error.LoadingFile            = An error occurred when loading the file\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Could not modify commodity\nMessage.Error.ModifyCurrency         = Could not modify currency\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = File closed\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Default locale changed to:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = Preparing for shutdown\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Report modules were loaded successfully\nMessage.ReportWait                   = Please wait, Loading report modules\nMessage.RestartLocale                = Restart for locale change to take effect\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = Enter starting and ending dates\nMessage.StoreBackup                  = Saving backup to:\nMessage.StoreComplete                = File store is complete\nMessage.StoreWait                    = Waiting for file save to complete\nMessage.TXTFile                      = Text Files (*.txt)\nMessage.TransactionAccountLocked     = Transaction add failed, Destination account(s) are locked\nMessage.TransactionAdd               = Transaction added\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transaction removed\nMessage.TransactionRemoveLocked      = Transaction removal failed, Destination account(s) are locked\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = Commodity is in use\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Currency is in use\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\\n\\nDo you want to continue?\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bank Accounts\nName.ExpenseAccounts = Expense Accounts\nName.IncomeAccounts  = Income Accounts\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = From {0,date,long} To {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Page {0} of {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minutes\nPeriod.15Min     = 15 Minutes\nPeriod.1Day      = 1 Day\nPeriod.1Hr       = 1 Hour\nPeriod.2Hr       = 2 Hours\nPeriod.30Min     = 30 Minutes\nPeriod.5Min      = 5 Minutes\nPeriod.8Hr       = 8 Hours\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Daily\nPeriod.Monthly   = Monthly\nPeriod.NextStart = Next time jGnash starts\nPeriod.None      = None\nPeriod.OnlyOnce  = Only once\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Weekly\nPeriod.Yearly    = Yearly\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = About\nTab.Accounts        = Accounts\nTab.Adjust          = Adjust\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Charge\nTab.Credit          = Credit\nTab.Credits         = Credits\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = Day\nTab.Debit           = Debit\nTab.Formats         = Formats\nTab.GPLLicense      = GPL License\nTab.General         = General\nTab.LGPLLicense     = LGPL License\nTab.License         = License\nTab.Month           = Month\nTab.Network         = Network\nTab.None            = None\nTab.Payment         = Payment\nTab.Register        = Register\nTab.Reminders       = Reminders\nTab.Report          = Report\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = System Information\nTab.Transfer        = Transfer\nTab.Week            = Week\nTab.Year            = Year\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = About\nTitle.AccountBalance             = Account Balance\nTitle.AccountFilter              = Account Filters\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Account Information\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Account Securities\nTitle.AddRemCurr                 = Add / Remove Currencies\nTitle.AmortizationSetup          = Amortization Setup\nTitle.Archive                    = Archive\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Automatic Save\nTitle.Available                  = Available\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Backing Store\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Check Designer\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Column Visibility\nTitle.Colors                     = Colors\nTitle.CommoditiesSecurities      = Securities\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirm\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Create / Modify Securities\nTitle.Credits                    = Credits\nTitle.Currencies                 = Currencies\nTitle.Current                    = Current\nTitle.CutOffDate                 = Select Cut Off Date\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Debits\nTitle.DefDefCurr                 = Define Default Currency\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Display\nTitle.DuplicateTransaction       = Duplicate Transaction\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = End-Of-Month Account Balance\nTitle.EnterPassword              = Enter Password\nTitle.Entry                      = Entry\nTitle.Error                      = Error\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Exchange Rate\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filters\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Frequency\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historical Import\nTitle.IEXCloud                   = IEX Cloud\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Import a partial QIF file\nTitle.ImpSum                     = Import Summary\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = Information\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Modify QIF Transactions\nTitle.ModifyAccount              = Modify Account\nTitle.ModifyCurrencies           = Modify Currencies\nTitle.ModifyReminder             = Modify Reminder\nTitle.ModifySecHistory           = Modify Securities History\nTitle.ModifyTransaction          = Modify Transaction\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = New Account\nTitle.NewBudget                  = New Budget\nTitle.NewFile                    = Create a New File\nTitle.NewPassword                = New Password\nTitle.NewReminder                = New Reminder\nTitle.NewTrans                   = New Transaction\nTitle.Notes                      = Notes\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Open\nTitle.Options                    = Options\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Page Setup\nTitle.ParentAccount              = Parent Account\nTitle.PercentDist                = Percent Distribution\nTitle.PercentExpense             = Percent Expense\nTitle.PercentIncome              = Percent Income\nTitle.PleaseWait                 = Please Wait\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profit and Loss Statement\nTitle.ReconcileSettings          = Reconcile Settings\nTitle.Reminder                   = Reminder\nTitle.Reminders                  = Reminders\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Save As\nTitle.SaveFile                   = Save File\nTitle.SecurityHistory            = Security History\nTitle.SelAccount                 = Select Account\nTitle.SelAvailCurr               = Select Available Currencies\nTitle.SelDate                    = Select a Date\nTitle.SelDefCurr                 = Select Default Currency\nTitle.SelDefLocale               = Select Default Locale\nTitle.SelDestAccount             = Select Destination Account\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Select Equity Account\nTitle.SelFile                    = Select File\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = Select Color\nTitle.Selected                   = Selected\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaction\nTitle.Startup                    = Startup\nTitle.Steps                      = Steps\nTitle.Success                    = Success\nTitle.Summary                    = Summary\nTitle.TagManager                 = Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaction\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Transaction List\nTitle.TransactionSetup           = Transaction Setup\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visible\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Account list\nToolTip.AccountRegister                      = Account register\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Change column visibility\nToolTip.ConvertSEntry                        = Change this transaction into a Double Entry transaction\nToolTip.DeleteAccount                        = Delete Account\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filter accounts\nToolTip.FilterAccounts                       = Filter by account type\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = Only integers allowed\nToolTip.ModifyAccount                        = Modify account\nToolTip.NewAccount                           = New Account\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = Print register report\nToolTip.ReconcileAccount                     = Reconcile Account\nToolTip.Reminders                            = Reminders\nToolTip.ResizeColumns                        = Resize Columns\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Number of digits to the right of the decimal\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Create a backup with timestamp when closed\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Open a new account register\n\nTransaction.AddShare        = Add Shares (Adjust)\nTransaction.BuyShare        = Buy Shares\nTransaction.Dividend        = Dividend\nTransaction.DoubleEntry     = Double Entry\nTransaction.MergeShare      = Stock Merge\nTransaction.ReinvestDiv     = Reinvest Dividend\nTransaction.RemoveShare     = Remove Shares (Adjust)\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Sell Shares\nTransaction.SingleEntry     = Single Entry\nTransaction.Split           = Split Transaction\nTransaction.SplitEntry      = Split Transaction Entry\nTransaction.SplitShare      = Stock Split\nTransaction.TransferIn      = Transfer Cash In\nTransaction.TransferOut     = Transfer Cash Out\n\nWord.Add             = Add\nWord.All             = All\nWord.Balance         = Balance\nWord.Buy             = Buy\nWord.Commodity       = Commodity\nWord.Copy            = Copy\nWord.Description     = Description\nWord.Difference      = Difference\nWord.Dividend        = Dividend\nWord.Exchange        = Exchange\nWord.Fees            = Fees\nWord.GrossExpense    = Gross Expense\nWord.GrossIncome     = Gross Income\nWord.Interest        = Interest\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = Merge\nWord.Monthly         = By month\nWord.Name            = Name\nWord.NetIncome       = Net Income\nWord.NetWorth        = Net Worth\nWord.NewBudget       = New Budget\nWord.None            = None\nWord.Quarterly       = Quarterly\nWord.ReInvDiv        = Reinvest Dividend\nWord.Remove          = Remove\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Security\nWord.Sell            = Sell\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totals\nWord.Yearly          = Yearly\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_cs.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Aktiva\nAccountType.Bank             = Banka\nAccountType.Cash             = Hotovost\nAccountType.Checking         = \\u0160ekov\\u00FD\nAccountType.Credit           = Kreditn\\u00ED\nAccountType.Equity           = Hodnota\nAccountType.Expense          = V\\u00FDdaje\nAccountType.Income           = P\\u0159\\u00EDjmy\nAccountType.Investment       = Investice\nAccountType.Liability        = Z\\u00E1vazek\nAccountType.MoneyMarket      = pen\\u011B\\u017En\\u00EDho trhu\nAccountType.Mutual           = Spole\\u010Dn\\u00FD fond\nAccountType.Root             = Root\nAccountType.SimpleInvestment = Inversi\\u00F3n sencilla\n\nButton.AccTerms                = Pou\\u017E\\u00EDt \\u00FA\\u010Detn\\u00ED term\\u00EDny\nButton.Accounts                = Polo\\u017Eky\nButton.AckSel                  = Potvrdit p\\u0159\\u00EDjem\nButton.Add                     = P\\u0159idat\nButton.AllDates                = V\\u0161echny term\\u00EDny\nButton.Amortize                = Odepisovat\nButton.AnimationsEnabled       = Aktivovat anima\\u010Dn\\u00ED efekty\nButton.AnyStatus               = Jak\\u00E9koliv Status\nButton.Apply                   = Pou\\u017E\\u00EDt\nButton.AssetAccounts           = majetkov\\u00FDch \\u00FA\\u010Dtech\nButton.AutoReconcile           = Automatically Reconcile Split Transactions\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = Zp\\u011Bt\nButton.BankAccounts            = Bankovn\\u00ED \\u00DA\\u010Dty\nButton.BudgetMgr               = rozpo\\u010Det mana\\u017Eer\nButton.Budgeting               = rozpo\\u010Dtov\\u00E1n\\u00ED\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Zru\\u0161it\nButton.CheckForUpdates         = Kontrolovat nov\\u00E9 aktualizace\nButton.CheckReminders          = Zkontrolovat upom\\u00EDnky\nButton.Clear                   = Smazat\nButton.ClearAll                = Clear All\nButton.Cleared                 = Z\\u00FA\\u010Dtov\\u00E1no\nButton.Close                   = Zav\\u0159\\u00EDt\nButton.Compare                 = porovnat\nButton.ConcatenateMemos        = kombinovat MEMOS\nButton.ConfirmReminderDelete   = Vy\\u017Eadovat potvrzen\\u00ED p\\u0159i maz\\u00E1n\\u00ED upom\\u00EDnky\nButton.ConfirmTransDelete      = Vy\\u017Eadovat potvrzen\\u00ED p\\u0159i maz\\u00E1n\\u00ED transakce\nButton.CopyToClip              = Kop\\u00EDrovat do schr\\u00E1nky\nButton.CreateTimeFile          = P\\u0159i ukon\\u010Den\\u00ED vytvo\\u0159it z\\u00E1lo\\u017En\\u00ED soubory\nButton.CreditAccounts          = \\u00FAv\\u011Brov\\u00FDch \\u00FA\\u010Dt\\u016F\nButton.Delete                  = Vymazat\nButton.DeleteAll               = Vymazat v\\u0161e\nButton.DeleteWeekends          = smazat v\\u00EDkendy\nButton.DetailSplits            = Show Split Details\nButton.Duplicate               = Duplikovat\nButton.Edit                    = Upravit\nButton.EnableAutoComplete      = Povolit automatick\\u00E9 dopl\\u0148ov\\u00E1n\\u00ED\nButton.Enabled                 = Povoleno\nButton.EndingBalance           = Kone\\u010Dn\\u00FD z\\u016Fstatek\nButton.Enter                   = Vlo\\u017Eit\nButton.EnterDaysBefore         = Automaticky vlo\\u017Eit po\\u010Det dn\\u00ED p\\u0159edem\nButton.ExcludeFromBudget       = Vylou\\u010Dit z rozpo\\u010Dt\\u016F\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = V\\u00FDdajov\\u00E9 polo\\u017Eky\nButton.Export                  = V\\u00FDvozn\\u00ED\nButton.ExportSpreadsheet       = Export Spreadsheet\nButton.Filter                  = Filtrovat\nButton.Finish                  = Dokon\\u010Dit\nButton.FinishLater             = Dokon\\u010Dit pozd\\u011Bji\nButton.ForceDefaultCurrency    = Pou\\u017Eit\\u00ED s\\u00EDly v\\u00FDchoz\\u00ED m\\u011Bnu\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = Po\\u017Eaduje ov\\u011B\\u0159en\\u00ED\nButton.Hidden                  = Skryt\\u00E9\nButton.HideAccount             = Skr\\u00FDt \\u00FA\\u010Det\nButton.HideLockedAccount       = Skr\\u00FDt zam\\u010Den\\u00E9 polo\\u017Eky\nButton.HidePlaceholderAccount  = Skr\\u00FDt z\\u00E1stupce kategorie\nButton.HideZeroBalance         = Skr\\u00FDt nulov\\u00E9 polo\\u017Eky\nButton.HistoricalFill          = Historick\\u00E1 Fill\nButton.Horizontal              = Horizont\\u00E1ln\\u00ED\nButton.IncludeSubAccounts      = Zahrnuj\\u00ED vedlej\\u0161\\u00ED \\u00FA\\u010Dty\nButton.IncomeAccounts          = P\\u0159\\u00EDjmov\\u00E9 polo\\u017Eky\nButton.IncomeAndExpense        = V\\u00FDnosy a n\\u00E1klady\nButton.Insert                  = Vlo\\u017Eit\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Invertovat v\\u00FDb\\u011Br\nButton.Jump                    = Uk\\u00E1zat\nButton.KeepFridays             = Udr\\u017Eujte p\\u00E1tky\nButton.KeepMondays             = Udr\\u017Eujte pond\\u011Blky\nButton.Landscape               = Landscape\nButton.Last120Days             = Posledn\\u00EDch 120 dn\\u016F\nButton.Last12Months            = Posledn\\u00EDch 12 m\\u011Bs\\u00EDc\\u016F\nButton.Last30Days              = Posledn\\u00EDch 30 dn\\u00ED\nButton.Last60Days              = Posledn\\u00EDch 60 dn\\u00ED\nButton.Last90Days              = Posledn\\u00EDch 90 dn\\u00ED\nButton.LiabilityAccounts       = \\u00FA\\u010Dty pasiv\nButton.Locked                  = Zamknout\nButton.MatchAccountOnly        = Match pomoc\\u00ED \\u00FA\\u010Dtu pouze ur\\u010Dit\\u00E9 transakce\nButton.MatchAllTrans           = Shodovat s vyu\\u017Eit\\u00EDm v\\u0161ech transakc\\u00ED\nButton.MatchCaseSensitive      = Match je velk\\u00E1 a mal\\u00E1 p\\u00EDsmena\nButton.Modify                  = Upravit\nButton.MonthlyBalance          = m\\u011Bs\\u00ED\\u010Dn\\u00ED Balance\nButton.New                     = Nov\\u00FD\nButton.NewEmpty                = New Empty\nButton.NewHist                 = New Historical\nButton.NewPayment              = Nov\\u00E1 platba\nButton.Next                    = Dal\\u0161\\u00ED\nButton.No                      = Ne\nButton.NoEndDate               = \\u017D\\u00E1dn\\u00E9 koncov\\u00E9 obdob\\u00ED\nButton.None                    = \\u017D\\u00E1dn\\u00FD\nButton.NotReconciled           = nesm\\u00ED\\u0159il\nButton.Ok                      = OK\nButton.Open                    = Otev\\u0159\\u00EDt\nButton.OpenLastOnStartup       = Otev\\u0159\\u00EDt posledn\\u00ED soubor p\\u0159i startu\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Page Setup\nButton.PlaceHolder             = Z\\u00E1stupce kategorie\nButton.Portrait                = Portrait\nButton.Print                   = Tisk\nButton.PrintSample             = Tisknout vzorek\nButton.Properties              = Vlastnosti\nButton.Reconcile               = Potvrzen\\u00ED\nButton.ReconcileBoth           = V\\u0161echny polo\\u017Eky maj\\u00ED stejn\\u00FD stav potvrzen\\u00ED\nButton.ReconcileDisable        = Zak\\u00E1zat automatick\\u00E9 potvrzov\\u00E1n\\u00ED\nButton.ReconcileIncomeExpense  = Automaticky potvrdit p\\u0159\\u00EDjmov\\u00E9 a v\\u00FDdajov\\u00E9 polo\\u017Eky\nButton.Reconciled              = Potvrzeno od banky\nButton.Refresh                 = Obnovit\nButton.RegDate                 = Pamatovat si datum posledn\\u00ED transakce\nButton.Register                = Z\\u00E1znam\nButton.RegisterFollowsList     = Register Follows Account List\nButton.RememberPassword        = Zapamatovat heslo\nButton.RemindLater             = P\\u0159ipomenout pozd\\u011Bji\nButton.Reminders               = Upom\\u00EDnky\nButton.RemoteServer            = Vzd\\u00E1len\\u00FD server\nButton.Remove                  = Odebrat\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Zm\\u011Bna velikosti\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = B\\u011Bh Balance\nButton.Save                    = Ulo\\\\u017Eit\nButton.SaveFilters             = Ulo\\u017Eit filtry\nButton.SaveImage               = Save Image\nButton.Select                  = Vybrat\nButton.SelectAll               = Vybrat v\\u0161e\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Uk\\u00E1zat v\\u0161echny komodity\nButton.ShowEmptyAccounts       = Uk\\u00E1z\\u00E1t polo\\u017Eky s nulovou bilanc\\u00ED\nButton.ShowPercentValues       = Uk\\u00E1zat procentu\\u00E1ln\\u00ED hodnoty\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Rozd\\u011Blit\nButton.Start                   = Za\\u010D\\u00E1tek\nButton.Stop                    = Zastavit\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Motivy povoleny\nButton.Timestamp               = \\u010Casov\\u00E1 zn\\u00E1mka\nButton.Today                   = Dnes\nButton.UpdateCurrenciesStartup = P\\u0159i spu\\u0161t\\u011Bn\\u00ED aktualizovat kurzy m\\u011Bn\nButton.UpdateOnline            = Aktualizovat online\nButton.UpdateSecuritiesStartup = P\\u0159i zapnut\\u00ED aktualizovat obligace\nButton.UseDailyRate            = Use Daily Periodic Rate\nButton.UseEncryption           = Use Encryption\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Use Long Names\nButton.UseProxy                = Pou\\u017E\\u00EDt proxy\nButton.UseRegexForFilter       = Pou?\\u00EDt regul\\u00E1rn\\u00ED v\\u00FDrazy pro filtry\nButton.Vertical                = Vertik\\u00E1ln\\u00ED\nButton.Yes                     = Ano\nButton.Zoom                    = Uk\\u00E1zat\n\nColumn.Account                        = Polo\\u017Eka\nColumn.AccountName                    = Jm\\u00E9no polo\\u017Eky\nColumn.Action                         = Akce\nColumn.Actual                         = Actual\nColumn.Amount                         = \\u010C\\u00E1stka\nColumn.Approve                        = Schv\\u00E1lit\nColumn.Balance                        = Bilance\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Poplatek\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = Commodity\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Credit\nColumn.Currency                       = M\\u011Bna\nColumn.Date                           = Datum\nColumn.Day                            = Den\nColumn.Debit                          = Debit\nColumn.Decrease                       = Decrease\nColumn.Deposit                        = Z\\u016Fstatek\nColumn.Description                    = Popis\nColumn.Due                            = Due\nColumn.Enabled                        = Povoleno\nColumn.Entries                        = Z\\u00E1znamy\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Kurz\nColumn.Expense                        = V\\u00FDdaje\nColumn.Freq                           = Frekvence\nColumn.Gain                           = Zisk\nColumn.High                           = High\nColumn.Income                         = P\\u0159\\u00EDjem\nColumn.Increase                       = Increase\nColumn.Investment                     = Investment\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Ztr\\u00E1ty\nColumn.Low                            = Low\nColumn.Memo                           = Pozn\\u00E1mky\nColumn.MktValue                       = Tr\\u017En\\u00ED hodnota\nColumn.Month                          = M\\u011Bs\\u00EDc\nColumn.Num                            = Num\nColumn.Payee                          = P\\u0159\\u00EDjemce platby\nColumn.Payment                        = Platba\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Cena\nColumn.Print                          = Tisk\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Po\\u010Det\nColumn.Rebate                         = Sr\\u00E1\\u017Eka\nColumn.Receive                        = P\\u0159\\u00EDjem\nColumn.ReconciledBalance              = Potvrzen\\u00E1 bilance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Mno\\u017Estv\\u00ED\nColumn.Short.RealizedGain             = R Zisk\nColumn.Short.RealizedGainPercentage   = R Zisk %\nColumn.Short.TotalGain                = T Zisk\nColumn.Short.TotalGainPercentage      = T Zisk %\nColumn.Short.UnrealizedGain           = U Zisk\nColumn.Short.UnrealizedGainPercentage = U Zisk %\nColumn.Spend                          = Spend\nColumn.Timestamp                      = \\u010Casov\\u00E9 raz\\u00EDtko\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Typ\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = V\\u00FDb\\u011Br\n\nDataStoreType.Bxds = bin\\u00E1rn\\u00ED File\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML soubor\n\nItem.Amount         = Amount\nItem.CashDeposit    = Cash Deposit\nItem.CashWithdrawal = Cash Withdrawal\nItem.Date           = Datum\nItem.EFT            = EFT\nItem.Memo           = Pozn\\u00E1mky\nItem.NextNum        = Next #\nItem.Payee          = P\\u0159\\u00EDjemce platby\nItem.Trans          = Trans\n\nLabel.AccentColor         = Accent Barva:\nLabel.Account             = Polo\\u017Eka:\nLabel.AccountCode         = \\u00DA\\u010Det k\\u00F3d:\nLabel.AccountNumber       = \\u010C\\u00EDslo \\u00FA\\u010Dtu:\nLabel.AccountOptions      = Mo\\u017Enosti \\u00FA\\u010Dtu:\nLabel.AccountSeparator    = Odd\\u011Blova\\u010D \\u00FA\\u010Dt\\u016F:\nLabel.AccountStatus       = Stav \\u00FA\\u010Dtu:\nLabel.AccountType         = Typ \\u00FA\\u010Dtu:\nLabel.Action              = Akce:\nLabel.Amount              = \\u010C\\u00E1stka:\nLabel.AnIntRate           = Ro\\u010Dn\\u00ED \\u00FArokov\\u00E1 m\\u00EDra :\nLabel.Available           = Dostupn\\u00E1 \\u010D\\u00E1stka:\nLabel.Balance             = Bilance:\nLabel.BankAccount         = Bankovn\\u00ED \\u00FA\\u010Det:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Hlavn\\u00ED \\u00FA\\u010Det:\nLabel.BaseColor           = Z\\u00E1kladn\\u00ED barva:\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = Hotovostn\\u00ED bilance:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Commodity:\nLabel.CompDaysPerYear     = Compounding Days per Year:\nLabel.CompPerTerm         = Compounding Periods per Year:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Count:\nLabel.CreateCurr          = Vytvo\\u0159it vlastn\\u00ED m\\u011Bnu:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Currency / Commodity:\nLabel.Currencies          = M\\u011Bny:\nLabel.Currency            = M\\u011Bna:\nLabel.Current             = Current:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Um\\u00EDst\\u011Bn\\u00ED datab\\u00E1ze:\nLabel.DatabaseServer      = Datab\\u00E1zov\\u00FD server:\nLabel.Date                = Datum:\nLabel.DateFormat          = Form\\u00E1t data:\nLabel.DaysPastDue         = Dn\\u016F po splatnosti:\nLabel.DefaultCurrency     = V\\u00FDchoz\\u00ED m\\u011Bna:\nLabel.DelaySec            = Delay (seconds)\nLabel.Description         = Popis:\nLabel.DestAccount         = C\\u00EDlov\\u00E1 polo\\u017Eka:\nLabel.Difference          = Rozd\\u00EDl:\nLabel.Dividend            = Dividenda:\nLabel.EndDate             = Konec obdob\\u00ED:\nLabel.EndOn               = Konec:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Kone\\u010Dn\\u00E1 bilance:\nLabel.EquityAccount       = Equity Account:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Ka\\u017Ed\\u00FD:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Sm\\u011Bnn\\u00FD kurz:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Poplatky:\nLabel.FeesAccount         = Fees Account:\nLabel.FileName            = Jm\\u00E9no souboru:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Datum prvn\\u00ED platby:\nLabel.FocusColor          = Zam\\u011B\\u0159it Barva:\nLabel.Format              = Format:\nLabel.Frequency           = Frekvence:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Zisky:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Height:\nLabel.High                = High:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = P\\u0159\\u00EDjmov\\u00E9 polo\\u017Eky:\nLabel.InterestAccount     = \\u00DArokov\\u00E9 polo\\u017Eky:\nLabel.LastOccurrence      = Posledn\\u00ED v\\u00FDskyt:\nLabel.Layout              = Rozlo\\u017Een\\u00ED:\nLabel.Left                = Left:\nLabel.LoanTerm            = D\\u00E9lka p\\u016Fj\\u010Dky (m\\u011Bs\\u00EDce):\nLabel.Low                 = Low:\nLabel.MarketValue         = Tr\\u017En\\u00ED hodnota:\nLabel.MaxBackupCount      = Maxim\\u00E1ln\\u00ED po\\u010Det z\\u00E1lo\\u017En\\u00EDch soubor\\u016F:\nLabel.Memo                = Pozn\\u00E1mky:\nLabel.Monospace           = P\\u00EDsmo se stejnou rozte\\u010D\\u00ED:\nLabel.Name                = Jm\\u00E9no:\nLabel.NetIncome           = \\u010Cist\\u00FD p\\u0159\\u00EDjem:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Datum p\\u0159\\u00ED\\u0161t\\u00ED platby:\nLabel.Notes               = Pozn\\u00E1mky:\nLabel.NumTrans            = \\u010C\\u00EDslo transakce:\nLabel.Number              = \\u010C\\u00EDslo:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Po\\u010D\\u00E1te\\u010Dn\\u00ED datum:\nLabel.OpeningBalance      = \\u00DAvodn\\u00ED bilance:\nLabel.OrigLoanAmt         = P\\u016Fvodn\\u00ED v\\u00FD\\u0161e \\u00FAv\\u011Bru:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Heslo:\nLabel.Path                = Cesta\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Platby za rok:\nLabel.Payee               = P\\u0159\\u00EDjemce platby:\nLabel.Period              = Period\nLabel.Port                = Port:\nLabel.Prefix              = Prefix:\nLabel.Price               = Cena:\nLabel.Proportional        = Proporcion\\u00E1ln\\u00ED:\nLabel.Quantity            = Mno\\u017Estv\\u00ED:\nLabel.QuoteSource         = Zdroj:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Potvrzen\\u00E1 bilance:\nLabel.RemindLater         = P\\u0159ipomenout znovu po:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Opakovat v\\u017Edy:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = M\\u011Bna:\nLabel.Resolution          = Rozli\\u0161en\\u00ED:\nLabel.ReturnOfCapital     = N\\u00E1vratnost kapit\\u00E1lu:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Des. m\\u00EDst:\nLabel.Securities          = Obligace:\nLabel.Security            = Obligace:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Uk\\u00E1zat pr\\u00E1zdn\\u00E9 polo\\u017Eky\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Za\\u010D\\u00E1tek obdob\\u00ED:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Start Position:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Statement Date:\nLabel.StorageType         = Zp\\u016Fsob evidence:\nLabel.Suffix              = Suffix:\nLabel.Symbol              = Symbol:\nLabel.TargetBalance       = C\\u00EDlov\\u00E1 bilance:\nLabel.Top                 = Top:\nLabel.Total               = Celkem:\nLabel.Transaction         = Transakce:\nLabel.TransferFrom        = P\\u0159evod z:\nLabel.TransferTo          = P\\u0159evod do:\nLabel.Type                = Typ:\nLabel.Units               = Units:\nLabel.UserName            = U\\u017Eivatelsk\\u00E9 jm\\u00E9no:\nLabel.Value               = Value:\nLabel.Verify              = Ov\\u011B\\u0159it:\nLabel.VerifyPassword      = Ov\\u011B\\u0159it heslo:\nLabel.Visible             = Viditeln\\u00FD:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = Soubory XML\nLabel.Year                = Year:\nLabel.jGnashFiles         = Soubory jGnash\n\nMenu.About.Name                       = O progr_amu\\u2026\nMenu.About.Tooltip                    = Informace o jGnash\nMenu.Account.Name                     = \\u00DA\\u010Dty\nMenu.AccountRegister.Name             = V\\u00FDpis...\nMenu.AddRemoveCurrency.Name=P\\u0159idat/Odebrat\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Aktualizovat v\\u0161echny m\\u011Bny\nMenu.BackgroundCurrencyUpdate.Tooltip = Aktualizovat v\\u0161echny sm\\u011Bnn\\u00E9 kurzy na pozad\\u00ED\nMenu.BackgroundSecurityUpdate.Name    = Aktualizovat v\\u0161echny obligace\nMenu.BackgroundSecurityUpdate.Tooltip = Aktualizovat ceny obligac\\u00ED na pozad\\u00ED\nMenu.BalanceSheet.Name                = Bilance...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Zav\\u0159\\u00EDt\nMenu.Close.Name                       = _Zav\\u0159\\u00EDt\nMenu.Close.Tooltip                    = Zav\\u0159\\u00EDt aktu\\u00E1ln\\u00ED soubor\nMenu.CloseAllWindows.Name             = Zav\\u0159\\u00EDt v\\u0161echna okna\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = Kop\\u00EDrovat\nMenu.Copy.Tooltip                     = Vytvo\\u0159it kopii vybran\\u00E9 polo\\u017Eky\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = M\\u011Bny\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Zm\\u011Bnit v\\u00FDchoz\\u00ED...\nMenu.DefaultCurrency.Tooltip          = Zm\\u011Bnit v\\u00FDchoz\\u00ED m\\u011Bnu\nMenu.Delete.Name                      = Vymazat\nMenu.Duplicate.Name                   = Duplikovat\nMenu.Edit.Name                        = \\u00DApravy\nMenu.EditTranNumList.Name             = Upravit \\u010D\\u00EDslov\\u00E1n\\u00ED transakc\\u00ED\nMenu.Exit.Name                        = _Ukon\\u010Dit\nMenu.Exit.Tooltip                     = Opustit jGnash\nMenu.Export.Name                      = _Exportovat\nMenu.ExportAccounts.Name              = Exportovat \\u00FA\\u010Dty...\nMenu.File.Archive                     = Archive...\nMenu.File.Name                        = _Soubor\nMenu.Filter.Name                      = _Filtry\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = N\\u00E1pov\\u011Bda\nMenu.Hide.Name                        = Skr\\u00FDt\nMenu.HistoryChart.Name                = Historick\\u00E1 tabulka...\nMenu.HistoryCommodity.Name            = _Historie ...\nMenu.HistoryImport.Name               = Historical Import...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Graf P\\u0159\\u00EDjmy / V\\u00FDdaje...\nMenu.Import.Name                      = _Importovat\nMenu.ImportAccounts.Name              = Importovat polo\\u017Eky...\nMenu.ImportAccounts.Tooltip           = Importovat soubor polo\\u017Eek jGnash\nMenu.ImportJgnash.Name                = Importovat jGnash...\nMenu.ImportJgnash.Tooltip             = Importovat soubory typu jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = Sezn_am polo\\u017Eek\\u2026\nMenu.Locale.Name                      = Zm\\u011Bnit lok\\u00E1ln\\u00ED nastaven\\u00ED...\nMenu.LookAndFeel.Name                 = Vzhled\nMenu.MarkAs.Name                      = Mark As\nMenu.Modify.Name                      = Upravit\nMenu.ModifyCommodity.Name             = Vytvo\\u0159it / Upravit...\nMenu.ModifyCurrency.Name              = Upravit...\nMenu.ModifyExchangeRates.Name         = Upravit sm\\u011Bnn\\u00E9 kurzy...\nMenu.MonthBalance.Name                = M\\u011Bs\\u00ED\\u010Dn\\u00ED bilance...\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = Bilance na konci m\\u011Bs\\u00EDce...\nMenu.MonthEndBalanceCSV.Name          = Bilance na konci m\\u011Bs\\u00EDce (CSV)...\nMenu.NetWorth.Name                    = \\u010Cist\\u00E1 hodnota...\nMenu.New.Name                         = _Nov\\u00FD\nMenu.New.Tooltip                      = Vytvo\\u0159it nov\\u00FD soubor\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Otev\\u0159\\u00EDt\\u2026\nMenu.Open.Tooltip                     = Vybrat soubor pro otev\\u0159en\\u00ED\nMenu.Option.Name                      = Mo\\u017Enosti...\nMenu.Option.Tooltip                   = Zm\\u011Bnit volby programu\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Graf P\\u0159\\u00EDjmy / V\\u00FDdaje podle p\\u0159\\u00EDjemce platby...\nMenu.PeriodicAccountBalance.Name      = Periodick\\u00E1 Z?statek na \\u00FA?tu\\u2026\nMenu.Portfolio.Name                   = Portfolio...\nMenu.ProfitLoss.Name                  = P\\u0159\\u00EDjmy a v\\u00FDdaje...\nMenu.ProfitLossTXT.Name               = P\\u0159\\u00EDjmy a v\\u00FDdaje (Text)...\nMenu.Reconcile.Name                   = Potvrzen\\u00ED\nMenu.Reconciled.Name                  = Potvrzeno\nMenu.RecurringList.Name               = Opakovan\\u00E9 transakce...\nMenu.Register.Name                    = Zaznamenat...\nMenu.Reports.Name                     = Zpr\\u00E1vy\nMenu.RunJavaScript.Name               = Spustit JavaScript...\nMenu.RunJavaScript.Tooltip            = Spustit JavaScript\nMenu.Save.Name                        = _Ulo\\u017Eit\nMenu.Save.Tooltip                     = Ulo\\u017Eit aktu\\u00E1ln\\u00ED soubor\nMenu.SaveAs.Name                      = Ulo\\u017Eit _jako...\nMenu.SaveAs.Tooltip                   = Ulo\\u017Eit aktu\\u00E1ln\\u00ED soubor pod nov\\u00FDm jm\\u00E9nem\nMenu.Securities.Name                  = _Obligace\nMenu.SetPassword.Name                 = Nastavit heslo...\nMenu.Show.Name                        = Uk\\u00E1zat\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Vzhled\nMenu.Tools.Name                       = N\\u00E1stroje\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Nepotvrzeno\nMenu.View.Name                        = Zobrazit\nMenu.Window.Name                      = Okno\n\nMessage.AcceptLicense                = Podm\\u00EDnk\\u00E1m licence rozum\\u00EDm a p\\u0159ij\\u00EDm\\u00E1m je.\nMessage.AccountAdd                   = Polo\\u017Eka p\\u0159id\\u00E1na\nMessage.AccountCode                  = Account code was not unique: The code was not changed\nMessage.AccountLocked                = Polo\\u017Eka uzam\\u010Dena\nMessage.AccountModify                = Polo\\u017Eka upravena\nMessage.AccountMoveFailed            = Polo\\u017Eka nemohla b\\u00FDt p\\u0159em\\u00EDst\\u011Bna\nMessage.AccountRemove                = Polo\\u017Eka odebr\\u00E1na\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = Automatick\\u00E9 ukl\\u00E1d\\u00E1n\\u00ED bylo vypnuto\nMessage.AutoSaveOn                   = Automatick\\u00E9 ukl\\u00E1d\\u00E1n\\u00ED bylo zapnuto\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Checking for new recurring events\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Shroma\\u017E\\u010Fov\\u00E1n\\u00ED dat\nMessage.CompilingReport              = Vytv\\u00E1\\u0159en\\u00ED zpr\\u00E1vy\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Vymazat vybran\\u00E9 transakce?\nMessage.ConfirmReminderDelete        = Smazat vybranou upom\\u00EDnku?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Smazat vybranou transakci?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = V\\u00FDchoz\\u00ED m\\u011Bna zm\\u011Bn\\u011Bna na:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine started\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Vytvo\\u0159en\\u00ED polo\\u017Eky se nezda\\u0159ilo\nMessage.Error.AccountRemove          = Odebr\\u00E1n\\u00ED polo\\u017Eky se nezda\\u0159ilo\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = P\\u0159id\\u00E1n\\u00ED komodity se nezda\\u0159ilo\nMessage.Error.AddCurrency            = P\\u0159id\\u00E1n\\u00ED m\\u011Bny se nezda\\u0159ilo\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Nejprve vytvo\\u0159te z\\u00E1kladn\\u00ED skupinu polo\\u017Eek\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = Soubor nenalezen\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = Licence nebyla p\\u0159ijata\nMessage.Error.LoadingFile            = Chyba p\\u0159i na\\u010D\\u00EDt\\u00E1n\\u00ED souboru\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Could not modify commodity\nMessage.Error.ModifyCurrency         = Could not modify currency\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = P\\u0159ipojen\\u00ED ke vzd\\u00E1len\\u00E9mu serveru se nezda\\u0159ilo\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Soubor uzav\\u0159en\nMessage.FileIsLocked                 = Soubor je pou\\u017E\\u00EDv\\u00E1n jinou aplikac\\u00ED\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = Vybran\\u00FD soubor nen\\u00ED platn\\u00FD\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Pros\\u00EDm \\u010Dekejte, import m\\u016F\\u017Ee chv\\u00EDli trvat\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash vy\\u017Eaduje JVM 11 nebo nov\\u011Bj\\u0161\\u00ED\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = V\\u00FDchoz\\u00ED m\\u00EDstn\\u00ED nastaven\\u00ED zm\\u011Bn\\u011Bno na:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Neopakuje se\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = Existuj\\u00EDc\\u00ED datab\\u00E1ze bude p\\u0159eps\\u00E1na\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Pros\\u00EDm \\u010Dekejte\nMessage.PrefFail                     = Star\\u00E9 p\\u0159edvolby nenalezeny\nMessage.PrepShutdown                 = P\\u0159ipravuji na vypnut\\u00ED\nMessage.ProcessingReportData         = Zpracov\\u00E1v\\u00E1m data pro v\\u00FDpis\nMessage.Proxy                        = Nastavuji http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Moduly pro zpr\\u00E1vy byly \\u00FAsp\\u011B\\u0161n\\u011B na\\u010Dteny\nMessage.ReportWait                   = Pros\\u00EDm \\u010Dekejte, na\\u010D\\u00EDt\\u00E1m moduly pro zpr\\u00E1vy\nMessage.RestartLocale                = Pro zm\\u011Bnu lok\\u00E1ln\\u00EDho nastaven\\u00ED restartujte program\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Vyhled\\u00E1v\\u00E1m, pros\\u00EDm \\u010Dekejte\nMessage.Shutdown                     = Vypnout\nMessage.StartEndDate                 = Vlo\\u017Ete za\\u010D\\u00E1tek a konec obdob\\u00ED\nMessage.StoreBackup                  = Ukl\\u00E1d\\u00E1m z\\u00E1lohu do:\nMessage.StoreComplete                = File store is complete\nMessage.StoreWait                    = Waiting for file store to complete\nMessage.TXTFile                      = Textov\\u00E9 soubory (*.txt)\nMessage.TransactionAccountLocked     = P\\u0159id\\u00E1n\\u00ED transakce se nezda\\u0159ilo, c\\u00EDlov\\u00E9 polo\\u017Eky jsou zam\\u010Deny\nMessage.TransactionAdd               = Transakce p\\u0159id\\u00E1na\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transakce odebr\\u00E1na\nMessage.TransactionRemoveLocked      = Odebr\\u00E1n\\u00ED transakce se nezda\\u0159ilo, c\\u00EDlov\\u00E9 polo\\u017Eky jsou zam\\u010Deny\nMessage.UninstallBad                 = Odinstalace se nezda\\u0159ila\nMessage.UninstallGood                = Odinstalace prob\\u011Bhla \\u00FAsp\\u011B\\u0161n\\u011B!\nMessage.UpdatedPrice                 = Aktualizovat cenu pro {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = Pou\\u017E\\u00EDv\\u00E1te verzi\nMessage.Warn.CommodityInUse          = Komodita se pou\\u017E\\u00EDv\\u00E1\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = M\\u011Bna se pou\\u017E\\u00EDv\\u00E1\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bankovn\\u00ED polo\\u017Eky\nName.ExpenseAccounts = V\\u00FDdajov\\u00E9 polo\\u017Eky\nName.IncomeAccounts  = P\\u0159\\u00EDjmov\\u00E9 polo\\u017Eky\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Od {0,date,long} Do {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Strana {0} z {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 minut\nPeriod.15Min     = 15 minut\nPeriod.1Day      = 1 den\nPeriod.1Hr       = 1 hodina\nPeriod.2Hr       = 2 hodiny\nPeriod.30Min     = 30 minut\nPeriod.5Min      = 5 minut\nPeriod.8Hr       = 8 hodin\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Denn\\u011B\nPeriod.Monthly   = M\\u011Bs\\u00ED\\u010Dn\\u011B\nPeriod.NextStart = P\\u0159i p\\u0159\\u00ED\\u0161t\\u00EDm startu\nPeriod.None      = Nikdy\nPeriod.OnlyOnce  = Jen jednou\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = T\\u00FDdn\\u011B\nPeriod.Yearly    = Ro\\u010Dn\\u011B\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = O programu\nTab.Accounts        = Accounts\nTab.Adjust          = P\\u0159izp\\u016Fsobit\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Charge\nTab.Credit          = Credit\nTab.Credits         = Credits\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = Den\nTab.Debit           = Debit\nTab.Formats         = Formats\nTab.GPLLicense      = GPL License\nTab.General         = Obecn\\u00E9\nTab.LGPLLicense     = LGPL License\nTab.License         = License\nTab.Month           = M\\u011Bs\\u00EDc\nTab.Network         = Network\nTab.None            = Nikdy\nTab.Payment         = Platba\nTab.Register        = Z\\u00E1znam\nTab.Reminders       = Upom\\u00EDnky\nTab.Report          = Zpr\\u00E1va\nTab.StartupShutdown = Zapnut\\u00ED / Vypnut\\u00ED\nTab.SysInfo         = Syst\\u00E9mov\\u00E9 informace\nTab.Transfer        = P\\u0159evod\nTab.Week            = T\\u00FDden\nTab.Year            = Rok\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividendy\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Zisky/(Ztr\\u00E1ty)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investi\\u010Dn\\u00ED poplatky\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Nezdaniteln\\u00E9 \\u00FAroky\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Zdaniteln\\u00E9 \\u00FAroky\nTag.Vat                    = DPH\n\nTitle.About                      = O programu\nTitle.AccountBalance             = \\u00DA\\u010Detn\\u00ED bilance\nTitle.AccountFilter              = Filtrovat polo\\u017Eky\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Informace o polo\\u017Ece\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Account Securities\nTitle.AddRemCurr                 = P\\u0159idat / Odebrat m\\u011Bny\nTitle.AmortizationSetup          = Amortization Setup\nTitle.Archive                    = Archiv\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Automatick\\u00E9 ukl\\u00E1d\\u00E1n\\u00ED\nTitle.Available                  = Dostupn\\u00E9\nTitle.BackgroundUpdate           = Aktualizace na pozad\\u00ED\nTitle.BackingStore               = Backing Store\nTitle.BalanceSheet               = Bilance\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = N\\u00E1vrh \\u0161ek\\u016F\nTitle.ChooseAccounts             = Vyberte typ polo\\u017Eek\nTitle.ColVis                     = Column Visibility\nTitle.Colors                     = Barvy\nTitle.CommoditiesSecurities      = Obligace\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Potvrzen\\u00ED\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Konzole\nTitle.CreateModifyCommodities    = Vytvo\\u0159it / Upravit obligace\nTitle.Credits                    = Credits\nTitle.Currencies                 = M\\u011Bny\nTitle.Current                    = M\\u011Bna\nTitle.CutOffDate                 = Select Cut Off Date\nTitle.DatabaseCfg                = Nastaven\\u00ED datab\\u00E1ze\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Dluhy\nTitle.DefDefCurr                 = Nastaven\\u00ED v\\u00FDchoz\\u00ED m\\u011Bny\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = V\\u00FDchoz\\u00ED\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Zobrazen\\u00ED\nTitle.DuplicateTransaction       = Duplicate Transaction\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Upravit sm\\u011Bnn\\u00E9 kurzy\nTitle.EndMonthBalance            = Bilance na konci m\\u011Bs\\u00EDce\nTitle.EnterPassword              = Zadejte heslo\nTitle.Entry                      = \\u00DAdaj\nTitle.Error                      = Chyba\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Sm\\u011Bnn\\u00FD kurz\nTitle.FileImport                 = Vyberte soubor, kter\\u00FD chete importovat\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filtry\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = V\\u00FDchoz\\u00ED p\\u00EDsmo\nTitle.Frequency                  = Frekvence\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historical Import\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Import a partial QIF file\nTitle.ImpSum                     = Importovat souhrm\nTitle.ImportOFX                  = Importovat soubor OFX\nTitle.ImportTransactions         = Importovat transakce ze souboru\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = informace\nTitle.InvFees                    = Investi\\u010Dn\\u00ED poplatky\nTitle.InvGainsLoss               = Zisky / ztr\\u00E1ty z investic\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Upravit transakce\nTitle.ModOFXTrans                = Upravit OFX transakce\nTitle.ModQIFTrans                = Upravit QIF transakce\nTitle.ModifyAccount              = Upravit polo\\u017Eku\nTitle.ModifyCurrencies           = Upravit m\\u011Bny\nTitle.ModifyReminder             = Upravit upom\\u00EDnku\nTitle.ModifySecHistory           = Upravit historii obligac\\u00ED\nTitle.ModifyTransaction          = Upravit transakci\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nov\\u00FD \\u00FA\\u010Det\nTitle.NewBudget                  = Nov\\u00FD Rozpo?et\nTitle.NewFile                    = Vytvo\\u0159it nov\\u00FD soubor\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nov\\u00E1 upom\\u00EDnka\nTitle.NewTrans                   = Nov\\u00E1 transakce\nTitle.Notes                      = Pozn\\u00E1mky\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Otev\\u0159\\u00EDt\nTitle.Options                    = Mo\\u017Enosti\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Page Setup\nTitle.ParentAccount              = Rodi\\u010Dovsk\\u00E1 polo\\u017Eka\nTitle.PercentDist                = Procentu\\u00E1ln\\u00ED rozd\\u011Blen\\u00ED\nTitle.PercentExpense             = Procentu\\u00E1ln\\u00ED v\\u00FDdaje\nTitle.PercentIncome              = Procentu\\u00E1ln\\u00ED p\\u0159\\u00EDjmy\nTitle.PleaseWait                 = Pros\\u00EDm \\u010Dekejte\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = P\\u0159ehled p\\u0159\\u00EDjm\\u016F a v\\u00FDdaj\\u016F\nTitle.ReconcileSettings          = Nastaven\\u00ED potvrzen\\u00ED z\\u00E1znam\\u016F\nTitle.Reminder                   = Upom\\u00EDnka\nTitle.Reminders                  = Upom\\u00EDnky\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Ulo\\u017Eit jako\nTitle.SaveFile                   = Ulo\\u017Eit soubor\nTitle.SecurityHistory            = Security History\nTitle.SelAccount                 = Vybrat \\u00FA\\u010Det\nTitle.SelAvailCurr               = Vyberte dostupn\\u00E9 m\\u011Bny\nTitle.SelDate                    = Vybrat datum\nTitle.SelDefCurr                 = Zvolte v\\u00FDchoz\\u00ED m\\u011Bnu\nTitle.SelDefLocale               = Zvolte v\\u00FDchoz\\u00ED jazykovou lokalizaci\nTitle.SelDestAccount             = Select Destination Account\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Select Equity Account\nTitle.SelFile                    = Vybrat soubor\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = V\\u00FDb\\u011Br barvy\nTitle.Selected                   = Selected\nTitle.Shutdown                   = Vypnout\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Rozd\\u011Blit transakci\nTitle.Startup                    = Startup\nTitle.Steps                      = Steps\nTitle.Success                    = \\u00DAsp?ch\nTitle.Summary                    = Souhrn\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transakce\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Seznam transakc\\u00ED\nTitle.TransactionSetup           = Nastaven\\u00ED transakce\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Nezachycen\\u00E1 vyj\\u00EDmka\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Viditeln\\u00E9\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Seznam polo\\u017Eek\nToolTip.AccountRegister                      = \\u00DA\\u010Detn\\u00ED z\\u00E1znamy\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Change column visibility\nToolTip.ConvertSEntry                        = Zm\\u011Bnit tuto transakci na transakci s dv\\u011Bma vklady\nToolTip.DeleteAccount                        = Vymazat polo\\u017Eku\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filtrovat polo\\u017Eky\nToolTip.FilterAccounts                       = Filtrovat podle typu polo\\u017Eek\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Upravit velikost p\\u00EDsma\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = ID mezin\\u00E1rodn\\u00EDch cenn\\u00FDch pap\\u00EDr\\u016F\nToolTip.IntegersOnly                         = pouze cel\\u00E1 \\u010D\\u00EDsla povolena\nToolTip.ModifyAccount                        = Upravit polo\\u017Eku\nToolTip.NewAccount                           = Nov\\u00E1 polo\\u017Eka\nToolTip.PageSetup                            = Nastaven\\u00ED str\\u00E1nky\nToolTip.PrintRegRep                          = Print register report\nToolTip.ReconcileAccount                     = Potvrdit polo\\u017Eku\nToolTip.Reminders                            = Upom\\u00EDnky\nToolTip.ResizeColumns                        = Zm\\u011Bnit velikost sloupv\\u016F\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Po\\u010Det desetinn\\u00FDch m\\u00EDst\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = P\\u0159i zav\\u0159en\\u00ED vytvo\\u0159it z\\u00E1lohu s \\u010Dasovou zn\\u00E1mkou\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Otev\\u0159\\u00EDt registr polo\\u017Eky\n\nTransaction.AddShare        = P\\u0159idat pod\\u00EDly (Upravit)\nTransaction.BuyShare        = Koupit pod\\u00EDly\nTransaction.Dividend        = Dividendy\nTransaction.DoubleEntry     = Double Entry\nTransaction.MergeShare      = Stock Merge\nTransaction.ReinvestDiv     = Reinvest Dividend\nTransaction.RemoveShare     = Odebrat pod\\u00EDly (Upravit)\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Prodat pod\\u00EDly\nTransaction.SingleEntry     = Single Entry\nTransaction.Split           = Split Transaction\nTransaction.SplitEntry      = Split Transaction Entry\nTransaction.SplitShare      = Stock Split\nTransaction.TransferIn      = Transfer Cash In\nTransaction.TransferOut     = Transfer Cash Out\n\nWord.Add             = P\\u0159idat\nWord.All             = V\\u0161e\nWord.Balance         = Bilance\nWord.Buy             = Koupit\nWord.Commodity       = Commodity\nWord.Copy            = Copy\nWord.Description     = Popis\nWord.Difference      = Difference\nWord.Dividend        = Dividend\nWord.Exchange        = Sm\\u011Bnit\nWord.Fees            = Poplatky\nWord.GrossExpense    = Hrub\\u00E9 n\\u00E1klady\nWord.GrossIncome     = Hrub\\u00FD p\\u0159\\u00EDjem\nWord.Interest        = \\u00DArok\nWord.Into            = na\nWord.Invalid         = Invalid\nWord.Merge           = j\\u00EDt\nWord.Monthly         = M\\u011Bs\\u00ED\\u010Dn\\u011B\nWord.Name            = Jm\\u00E9no\nWord.NetIncome       = \\u010Cist\\u00FD p\\u0159\\u00EDjem\nWord.NetWorth        = \\u010Cist\\u00E1 hodnota\nWord.NewBudget       = New Budget\nWord.None            = None\nWord.Quarterly       = \\u010Ctvrtletn\\u011B\nWord.ReInvDiv        = Reinvest Dividend\nWord.Remove          = Odebrat\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Obligace\nWord.Sell            = Sell\nWord.Split           = Split\nWord.Subtotal        = Mezisou\\u010Det\nWord.Total           = Sou\\u010Det\nWord.Totals          = Sou\\u010Dty\nWord.Yearly          = Ro\\u010Dn\\u011B\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_de.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Aktiva\nAccountType.Bank             = Bank\nAccountType.Cash             = Bargeld\nAccountType.Checking         = Scheckkonto\nAccountType.Credit           = Kreditkarte\nAccountType.Equity           = Eigenkapital\nAccountType.Expense          = Ausgabe\nAccountType.Income           = Einkommen\nAccountType.Investment       = Investment\nAccountType.Liability        = Verbindlichkeit\nAccountType.MoneyMarket      = Geldmarktfond\nAccountType.Mutual           = Investmentfonds\nAccountType.Root             = Wurzel\nAccountType.SimpleInvestment = Einfache Investition\n\nButton.AccTerms                = Buchf\\u00FChrungs-Begriffe benutzen\nButton.Accounts                = Konten\nButton.AckSel                  = Markierte best\\u00E4tigen\nButton.Add                     = Hinzuf\\u00FCgen\nButton.AllDates                = Alle Termine\nButton.Amortize                = Amortisierung\nButton.AnimationsEnabled       = Aktivieren Sie Animationseffekte\nButton.AnyStatus               = Jeder Status\nButton.Apply                   = Anwenden\nButton.AssetAccounts           = Verm\\u00F6genskonten\nButton.AutoReconcile           = Split-Buchungen automatisch abgleichen\nButton.AutoResizeColumns       = Automatische Spaltengr\\u00F6\\u00DFe\nButton.AvailSecurities         = Verf\\u00FCgbare Wertpapiere\nButton.Back                    = Zur\\u00FCck\nButton.BankAccounts            = Bankkonten\nButton.BudgetMgr               = Budgetmanager\nButton.Budgeting               = Budgetierung\nButton.CalcBal                 = Kontostand Berechnen\nButton.Cancel                  = Abbrechen\nButton.CheckForUpdates         = Suchen Sie nach neuen Updates\nButton.CheckReminders          = Erinnerungen pr\\u00FCfen\nButton.Clear                   = Zur\\u00FCcksetzen\nButton.ClearAll                = Alles zur\\u00FCcksetzen\nButton.Cleared                 = Best\\u00E4tigt\nButton.Close                   = Schlie\\u00DFen\nButton.Compare                 = Vergleichen\nButton.ConcatenateMemos        = Kombinieren Sie Memos\nButton.ConfirmReminderDelete   = Erinnerung l\\u00F6schen best\\u00E4tigen\nButton.ConfirmTransDelete      = Buchung l\\u00F6schen best\\u00E4tigen\nButton.CopyToClip              = In Zwischenablage kopieren\nButton.CreateTimeFile          = Beim Beenden Sicherheitskopie erstellen\nButton.CreditAccounts          = Kreditorenbuchhaltung\nButton.Delete                  = L\\u00F6schen\nButton.DeleteAll               = Alle l\\u00F6schen\nButton.DeleteWeekends          = Wochenenden l\\u00F6schen\nButton.DetailSplits            = Split Details anzeigen\nButton.Duplicate               = Duplizieren\nButton.Edit                    = Bearbeiten\nButton.EnableAutoComplete      = Auto-Vervollst\\u00E4ndigen Einschalten\nButton.Enabled                 = Aktiviert\nButton.EndingBalance           = Endsaldo\nButton.Enter                   = Erfassen\nButton.EnterDaysBefore         = Automatisch Anzahl Tage vorher eingeben\nButton.ExcludeFromBudget       = Aus Budgets ausschlie\\u00DFen\nButton.ExecuteNow              = Jetzt ausf\\\\u00FChren\nButton.ExpenseAccounts         = Ausgabenkonten\nButton.Export                  = Exportieren\nButton.ExportSpreadsheet       = Tabelle exportieren\nButton.Filter                  = Filter\nButton.Finish                  = Abschlie\\u00DFen\nButton.FinishLater             = Sp\\u00E4ter abschlie\\u00DFen\nButton.ForceDefaultCurrency    = Force Verwendung der Standardw\\u00E4hrung\nButton.ForceGC                 = Garbage Collection erzwingen\nButton.HTTPAuth                = Authentifizierung erforderlich\nButton.Hidden                  = Versteckt\nButton.HideAccount             = Konto verstecken\nButton.HideLockedAccount       = Gesperrte Konten ausblenden\nButton.HidePlaceholderAccount  = Platzhalterkonten ausblenden\nButton.HideZeroBalance         = Konten mit Saldo Null verstecken\nButton.HistoricalFill          = Historische F\\u00FCllung\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Unterkonten mit einbeziehen\nButton.IncomeAccounts          = Einnahmenkonten\nButton.IncomeAndExpense        = Einnahmen und Ausgaben\nButton.Insert                  = Einf\\u00FCgen\nButton.InvertBalances          = Salden invertieren\nButton.InvertSelection         = Selektion umkehren\nButton.Jump                    = Springen\nButton.KeepFridays             = Halten Sie Freitags\nButton.KeepMondays             = Halten Sie Montagen\nButton.Landscape               = Landscape\nButton.Last120Days             = Letzte 120 Tage\nButton.Last12Months            = Letzte 12 Monate\nButton.Last30Days              = Letzte 30 Tage\nButton.Last60Days              = Letzte 60 Tage\nButton.Last90Days              = Letzte 90 Tage\nButton.LiabilityAccounts       = Verbindlichkeiten\nButton.Locked                  = Gesperrt\nButton.MatchAccountOnly        = Abgleich mit nur konkreten Transaktionen\nButton.MatchAllTrans           = Match mit allen Transaktionen\nButton.MatchCaseSensitive      = Bei der Gro\\u00DF- / Kleinschreibung wird ber\\u00FCcksichtigt\nButton.Modify                  = \\u00C4ndern\nButton.MonthlyBalance          = Monatssaldo\nButton.New                     = Neu\nButton.NewEmpty                = Neue Leere\nButton.NewHist                 = Neu Historisch\nButton.NewPayment              = Neue Zahlung\nButton.Next                    = N\\u00E4chster\nButton.No                      = Nein\nButton.NoEndDate               = Kein Enddatum\nButton.None                    = Keiner\nButton.NotReconciled           = Nicht abgestimmt\nButton.Ok                      = OK\nButton.Open                    = \\u00D6ffnen\nButton.OpenLastOnStartup       = \\u00D6ffnen Sie die Datei beim Start\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Seite einrichten\nButton.PlaceHolder             = Platzhalter\nButton.Portrait                = Portrait\nButton.Print                   = Drucken\nButton.PrintSample             = Muster drucken\nButton.Properties              = Eigenschaften\nButton.Reconcile               = Abgleichen\nButton.ReconcileBoth           = Alle Konten der Buchung haben denselben Abgleich-Status\nButton.ReconcileDisable        = Automatisches Abgleichen abschalten\nButton.ReconcileIncomeExpense  = Einnahme- und Ausgabe-Konten automatisch abgleichen\nButton.Reconciled              = Abgeglichen\nButton.Refresh                 = Aktualisieren\nButton.RegDate                 = Letztes Buchungsdatum merken\nButton.Register                = Kontenregister\nButton.RegisterFollowsList     = Register folgt der Kontenliste\nButton.RememberPassword        = Passwort merken\nButton.RemindLater             = Sp\\u00E4ter erinnern\nButton.Reminders               = Erinnerungen\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Entfernen\nButton.RemoveOldBackups        = Alte Backups l\\u00F6schen\nButton.Rename                  = Umbenennen\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Alle Zur\\u00FCcksetzen\nButton.Resize                  = Gr\\u00F6\\u00DFe anpassen\nButton.RestoreDefault          = Standard wiederherstellen\nButton.RestoreLastTranTab      = Zuletzt genutzter Transaktions-Tab wiederherstellen\nButton.RoundToWhole            = Auf ganze Betr\\u00E4ge aufrunden\nButton.RunningBalance          = Fortlaufendes Saldo\nButton.Save                    = Speichern\nButton.SaveFilters             = Filter speichern\nButton.SaveImage               = Bild speichern\nButton.Select                  = Ausw\\u00E4hlen\nButton.SelectAll               = Alles Ausw\\u00E4hlen\nButton.SelectText              = Text beim Fokussieren selektieren\nButton.ShowCommodities         = Zeige alle Anlagen\nButton.ShowEmptyAccounts       = Zeige Konten mit Saldo Null\nButton.ShowPercentValues       = Zeige Prozent-Werte\nButton.ShowTimestamp           = Zeitstempel anzeigen\nButton.Splits                  = Splits\nButton.Start                   = Anfang\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = Look and Feel Animationen aktivieren\nButton.SumColVis               = Zeige Summen Spalte\nButton.SumRowVis               = Zeige Summen Reihe\nButton.ThemesEnabled           = Farbschemas aktiviert\nButton.Timestamp               = Zeitstempel\nButton.Today                   = Heute\nButton.UpdateCurrenciesStartup = Wechselkurse beim Start aktualisieren\nButton.UpdateOnline            = Online-Aktualisierung\nButton.UpdateSecuritiesStartup = Wertpapiere beim Start aktualisieren\nButton.UseDailyRate            = Tagess\\u00E4tze verwenden\nButton.UseEncryption           = Verschl\\u00FCsselung verwenden\nButton.UseFuzzyMatch           = Fuzzy Match verwenden\nButton.UseLongNames            = Lange Namen verwenden\nButton.UseProxy                = Proxy verwenden\nButton.UseRegexForFilter       = Verwenden Sie regul\\u00E4re Ausdr\\u00FCcke f\\u00FCr Filter\nButton.Vertical                = Vertikal\nButton.Yes                     = Ja\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Konto\nColumn.AccountName                    = Konten-Name\nColumn.Action                         = Aktion\nColumn.Actual                         = Tats\\u00E4chlich\nColumn.Amount                         = Betrag\nColumn.Approve                        = Akzeptieren\nColumn.Balance                        = Saldo\nColumn.Budgeted                       = Budgetiert\nColumn.Charge                         = Belastung\nColumn.Close                          = Schliessen\nColumn.Clr                            = Abg\nColumn.Code                           = Code\nColumn.Commodity                      = Anlage\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Haben\nColumn.Currency                       = W\\u00E4hrung\nColumn.Date                           = Datum\nColumn.Day                            = Tag\nColumn.Debit                          = Soll\nColumn.Decrease                       = Verringern\nColumn.Deposit                        = Einzahlung\nColumn.Description                    = Beschreibung\nColumn.Due                            = F\\u00E4llig\nColumn.Enabled                        = Eingeschaltet\nColumn.Entries                        = Eintr\\u00E4ge\nColumn.Event                          = Ereignis\nColumn.ExchangeRate                   = Wechselkurs\nColumn.Expense                        = Ausgabe\nColumn.Freq                           = H\\u00E4ufigkeit\nColumn.Gain                           = Gewinn\nColumn.High                           = H\\u00F6chst\nColumn.Income                         = Einkommen\nColumn.Increase                       = Erh\\u00F6hen\nColumn.Investment                     = Investment\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Verlust\nColumn.Low                            = Tiefst\nColumn.Memo                           = Notizen\nColumn.MktValue                       = Wert\nColumn.Month                          = Monat\nColumn.Num                            = Num\nColumn.Payee                          = Empf\\u00E4nger\nColumn.Payment                        = Zahlung\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Preis\nColumn.Print                          = Drucken\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Menge\nColumn.Rebate                         = Rabatt\nColumn.Receive                        = Empfangen\nColumn.ReconciledBalance              = Abgeglichener Saldo\nColumn.Remaining                      = \\u00DCbrig\nColumn.Script                         = Script\nColumn.Security                       = Wertpapier\nColumn.Short.InternalRateOfReturn     = ann. Rendite\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Mge\nColumn.Short.RealizedGain             = Real. Gewinn\nColumn.Short.RealizedGainPercentage   = Real. Gewinn %\nColumn.Short.TotalGain                = Tot. Gewinn\nColumn.Short.TotalGainPercentage      = Tot. Gewinn %\nColumn.Short.UnrealizedGain           = Unreal. Gewinn\nColumn.Short.UnrealizedGainPercentage = Unreal. Gain %\nColumn.Spend                          = Ausgeben\nColumn.Timestamp                      = Zeitstempel\nColumn.Total                          = Saldo\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Typ\nColumn.Value                          = Value\nColumn.Volume                         = Volumen\nColumn.Withdrawal                     = Abhebung\n\nDataStoreType.Bxds = Bin\\u00E4rdatei\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML-Datei\n\nItem.Amount         = Betrag\nItem.CashDeposit    = Bareinzahlung\nItem.CashWithdrawal = Barentnahme\nItem.Date           = Datum\nItem.EFT            = EFT\nItem.Memo           = Notiz\nItem.NextNum        = N\\u00E4chste Nr.\nItem.Payee          = Empf\\u00E4nger\nItem.Trans          = \\u00DCberweisung\n\nLabel.AccentColor         = Akzentfarbe:\nLabel.Account             = Konto:\nLabel.AccountCode         = Konto -Code:\nLabel.AccountNumber       = Konto-Nr:\nLabel.AccountOptions      = Konto-Optionen:\nLabel.AccountSeparator    = Konten-Trennzeichen:\nLabel.AccountStatus       = Konto-Status:\nLabel.AccountType         = Konto-Typ:\nLabel.Action              = Aktion:\nLabel.Amount              = Betrag:\nLabel.AnIntRate           = Zinssatz (pro Jahr):\nLabel.Available           = Verf\\u00FCgbar:\nLabel.Balance             = Saldo:\nLabel.BankAccount         = Bankkonto:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Hauptkonto:\nLabel.BaseColor           = Grundfarbe:\nLabel.Bottom              = Bottom:\nLabel.By                  = Von:\nLabel.CashBalance         = Bar-Saldo:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Anlage:\nLabel.CompDaysPerYear     = Verzinsungstage pro Jahr:\nLabel.CompPerTerm         = Verzinsungperioden pro Jahr:\nLabel.Compare             = Vergleichen:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Z\\u00E4hlen:\nLabel.CreateCurr          = Benutzerdefinierte W\\u00E4hrung:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = W\\u00E4hrungen / Anlagen:\nLabel.Currencies          = W\\u00E4hrungen:\nLabel.Currency            = W\\u00E4hrung:\nLabel.Current             = Aktuell:\nLabel.DatabaseBackend     = Datenbank Backend:\nLabel.DatabaseName        = Datenbank Ort:\nLabel.DatabaseServer      = Datenbank Server:\nLabel.Date                = Datum:\nLabel.DateFormat          = Datums-Format:\nLabel.DaysPastDue         = Tage \\u00FCberf\\u00E4llig:\nLabel.DefaultCurrency     = Standard-W\\u00E4hrung:\nLabel.DelaySec            = Verz\\u00F6gerung (Sekunden)\nLabel.Description         = Beschreibung:\nLabel.DestAccount         = Ziel-Konto:\nLabel.Difference          = Differenz:\nLabel.Dividend            = Dividende:\nLabel.EndDate             = End-Datum:\nLabel.EndOn               = Endet Am:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Schluss-Saldo:\nLabel.EquityAccount       = Eigenkapital-Konto:\nLabel.EscrowPmi           = Geb\\u00FChren, Vers. etc:\nLabel.Event               = Event:\nLabel.Every               = Jede(n):\nLabel.ExchangeAmount      = Wechselbetrag:\nLabel.ExchangeRate        = Wechselkurs:\nLabel.ExchangedAmount     = Wechselbetrag:\nLabel.Fees                = Geb\\u00FChren:\nLabel.FeesAccount         = Geb\\u00FChrenkonto:\nLabel.FileName            = Dateiname:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Datum der ersten Zahlung:\nLabel.FocusColor          = Fokus Farbe:\nLabel.Format              = Format:\nLabel.Frequency           = H\\u00E4ufigkeit:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gewinne:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = H\\u00F6he:\nLabel.High                = H\\u00F6chst:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Einkommenskonto:\nLabel.InterestAccount     = Zinsenkonto:\nLabel.LastOccurrence      = Letztes Vorkommen:\nLabel.Layout              = Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = Laufzeit (Monate):\nLabel.Low                 = Tiefst:\nLabel.MarketValue         = Marktwert:\nLabel.MaxBackupCount      = Maximale Anzahl an Sicherungskopien der Dateien:\nLabel.Memo                = Notizen:\nLabel.Monospace           = Feste Breite:\nLabel.Name                = Name:\nLabel.NetIncome           = Netto-Eink\\u00FCnfte:\nLabel.NewPassword         = Neues Passwort:\nLabel.NextPayDate         = Datum der n\\u00E4chsten Zahlung:\nLabel.Notes               = Notizen:\nLabel.NumTrans            = Anzahl Buchungen:\nLabel.Number              = Nummer:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Datum der Er\\u00F6ffnungs-Buchung:\nLabel.OpeningBalance      = Anfangs-Saldo:\nLabel.OrigLoanAmt         = Urspr\\u00FCnglicher Kreditbetrag:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Passwort:\nLabel.Path                = Pfad\nLabel.Pattern             = Muster:\nLabel.PayPerTerm          = Zahlungen pro Jahr:\nLabel.Payee               = Empf\\u00E4nger\nLabel.Period              = Period:\nLabel.Port                = Port:\nLabel.Prefix              = Pr\\u00E4fix\nLabel.Price               = Preis:\nLabel.Proportional        = Proportional:\nLabel.Quantity            = Menge:\nLabel.QuoteSource         = Quelle f\\u00FCr Kurse:\nLabel.ReceivingAccount    = Empfangendes Konto:\nLabel.ReconciledBalance   = Abgeglichener Saldo:\nLabel.RemindLater         = Wieder erinnern nach:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Wiedh. am:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Angegebene W\\u00E4hrung:\nLabel.Resolution          = Aufl\\u00F6sung:\nLabel.ReturnOfCapital     = Kapitalr\\u00FCckzahlung:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Komma-Stellen:\nLabel.Securities          = Wertpapiere:\nLabel.Security            = Wertpapier:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Zeige leere Konten\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Start-Datum:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Start-Position:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Datum des Kontoauszugs:\nLabel.StorageType         = Speicherungsart:\nLabel.Suffix              = Suffix:\nLabel.Symbol              = Symbol:\nLabel.TargetBalance       = Ziel-Saldo:\nLabel.Top                 = Top:\nLabel.Total               = Gesamt:\nLabel.Transaction         = Buchung:\nLabel.TransferFrom        = \\u00DCberweisen von:\nLabel.TransferTo          = \\u00DCberweisen nach:\nLabel.Type                = Typ:\nLabel.Units               = Units:\nLabel.UserName            = Benutzername:\nLabel.Value               = Value:\nLabel.Verify              = Best\\u00E4tigen:\nLabel.VerifyPassword      = Passwort best\\u00E4tigen:\nLabel.Visible             = Sichtbar:\nLabel.Volume              = Volumen:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML-Dateien\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Dateien\n\nMenu.About.Name                       = _Info\\u2026\nMenu.About.Tooltip                    = Information \\u00FCber jGnash\nMenu.Account.Name                     = Konto\nMenu.AccountRegister.Name             = Kontenregister\\u2026\nMenu.AddRemoveCurrency.Name           = _Hinzuf\\u00FCgen / L\\u00F6schen\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Alle W\\u00E4hrungen aktualisieren\nMenu.BackgroundCurrencyUpdate.Tooltip = Aktualisiert im Hintergrund alle Wechselkurse\nMenu.BackgroundSecurityUpdate.Name    = Alle Wertpapiere aktualisieren\nMenu.BackgroundSecurityUpdate.Tooltip = Aktualisiert im Hintergrund alle Kurse von Wertpapieren\nMenu.BalanceSheet.Name                = Bilanz\\u2026\nMenu.BaseColor.Name                   = Grundfarben \\u00E4ndern\\u2026\nMenu.BudgetManager.Name               = _Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Datenbank Passwort \\u00E4ndern\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Best\\u00E4tigt\nMenu.Close.Name                       = S_chlie\\u00DFen\nMenu.Close.Tooltip                    = Schlie\\u00DFe die aktive Datei\nMenu.CloseAllWindows.Name             = Schlie\\u00DFe alle Fenster\nMenu.ConfigImportFilters.Name         = Transaktions Import Filter konfigurieren\\u2026\nMenu.Console.Name                     = Konsolen-Fenster\\u2026\nMenu.Copy.Name                        = _Kopieren\nMenu.Copy.Tooltip                     = Erzeugt ein Duplikat des gew\\u00E4hlten Eintrags\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _W\\u00E4hrungen\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Stan_dard\\u2026\nMenu.DefaultCurrency.Tooltip          = Standard-W\\u00E4hrung \\u00E4ndern\nMenu.Delete.Name                      = L\\u00F6schen\nMenu.Duplicate.Name                   = Duplizieren\nMenu.Edit.Name                        = _Bearbeiten\nMenu.EditTranNumList.Name             = Buchungsnummernliste bearbeiten\nMenu.Exit.Name                        = B_eenden\nMenu.Exit.Tooltip                     = Beendet die Anwendung\nMenu.Export.Name                      = _Export\nMenu.ExportAccounts.Name              = Konten exportieren\\u2026\nMenu.File.Archive                     = Archivieren\\u2026\nMenu.File.Name                        = _Datei\nMenu.Filter.Name                      = Filter\nMenu.FontSize.Name                    = Standard Schriftgr\\u00F6\\u00DFe \\u00E4ndern\\u2026\nMenu.Help.Name                        = _Hilfe\nMenu.Hide.Name                        = Verstecken\nMenu.HistoryChart.Name                = Historisches Diagramm\\u2026\nMenu.HistoryCommodity.Name            = _Historie\\u2026\nMenu.HistoryImport.Name               = Historie importieren\\u2026\nMenu.IEBarChart.Name                  = Einnahmen-/Ausgaben-Balken-Diagramm\\u2026\nMenu.IEPieChart.Name                  = Einnahmen-/Ausgaben-Torten-Diagramm\\u2026\nMenu.Import.Name                      = _Import\\u2026\nMenu.ImportAccounts.Name              = Konten importieren\\u2026\nMenu.ImportAccounts.Tooltip           = Importiert eine jGnash Kontendatei\nMenu.ImportJgnash.Name                = jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Importiert jGnash 1.11.x Dateien\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Importiert OFX und QFX Dateien\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Springen\nMenu.ListOfAccounts.Name              = Konten_liste\\u2026\nMenu.Locale.Name                      = Regionale Einstellungen\\u2026\nMenu.LookAndFeel.Name                 = Darstellung\nMenu.MarkAs.Name                      = Markieren als\nMenu.Modify.Name                      = \\u00C4ndern\nMenu.ModifyCommodity.Name             = Erzeugen / \\u00C4ndern\\u2026\nMenu.ModifyCurrency.Name              = _\\u00C4ndern\\u2026\nMenu.ModifyExchangeRates.Name         = Wechselkurse bearbeiten\\u2026\nMenu.MonthBalance.Name                = Monats-Saldo\\u2026\nMenu.MonthBalanceCompare.Name         = Vergleich monatliche Ausgabe\\u2026\nMenu.MonthEndBalance.Name             = Monatsende-Saldo\\u2026\nMenu.MonthEndBalanceCSV.Name          = Monatsende-Saldo (CSV)\\u2026\nMenu.NetWorth.Name                    = Nettowert\\u2026\nMenu.New.Name                         = _Neu\\u2026\nMenu.New.Tooltip                      = Erzeugt eine neue Datei\nMenu.NewReminder.Name                 = Erinnerung erstellen\nMenu.Open.Name                        = \\u00D6_ffnen\\u2026\nMenu.Open.Tooltip                     = \\u00D6ffnet die angegebene Datei\nMenu.Option.Name                      = _Optionen\\u2026\nMenu.Option.Tooltip                   = Programmoptionen \\u00E4ndern\nMenu.PackDatabase.Name                = Datenbank komprimieren\\u2026\nMenu.PayeePieChart.Name               = Einnahmen/Ausgaben pro Empf\\u00E4nger Tortendiagramm\\u2026\nMenu.PeriodicAccountBalance.Name      = Periodische Kontostand\\u2026\nMenu.Portfolio.Name                   = Portfolio\\u2026\nMenu.ProfitLoss.Name                  = Gewinn und Verlust\\u2026\nMenu.ProfitLossTXT.Name               = Gewinn und Verlust (Text)\\u2026\nMenu.Reconcile.Name                   = Abgleichen\nMenu.Reconciled.Name                  = Abgeglichen\nMenu.RecurringList.Name               = Regelm\\u00E4\\u00DFige Buchungen\\u2026\nMenu.Register.Name                    = Konten_register\\u2026\nMenu.Reports.Name                     = Be_richte\nMenu.RunJavaScript.Name               = JavaScript\\u2026\nMenu.RunJavaScript.Tooltip            = JavaScript ausf\\u00FChren\nMenu.Save.Name                        = _Speichern\nMenu.Save.Tooltip                     = Speichern des aktuellen Dokuments\nMenu.SaveAs.Name                      = Speichern unter\\u2026\nMenu.SaveAs.Tooltip                   = Speichert das aktuelle Dokument unter einem neuen Namen\nMenu.Securities.Name                  = Anlagen\nMenu.SetPassword.Name                 = Passwort setzen\\u2026\nMenu.Show.Name                        = Anzeigen\nMenu.ShutdownServer.Name              = Server stoppen\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Farbschemas\nMenu.Tools.Name                       = _Extras\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Nicht abgeglichen\nMenu.View.Name                        = _Ansicht\nMenu.Window.Name                      = Fenster\n\nMessage.AcceptLicense                = Ich habe die Lizenzbedingungen gelesen, verstanden und akzeptiert.\nMessage.AccountAdd                   = Konto hinzugef\\u00FCgt\nMessage.AccountCode                  = Konto-Code war nicht eindeutig. Der Code wurde nicht ge\\u00E4ndert\nMessage.AccountLocked                = Konto ist gesperrt\nMessage.AccountModify                = Konto ge\\u00E4ndert\nMessage.AccountMoveFailed            = Konte das Konto nicht verschieben\nMessage.AccountRemove                = Konto entfernt\nMessage.AntiAlias                    = Aktiviere Schrift-Kantengl\\u00E4ttung!\nMessage.AutoSaveOff                  = Autom. Speichern wurde ausgeschaltet\nMessage.AutoSaveOn                   = Autom. Speichern wurde eingeschaltet\nMessage.CSVFile                      = Komma-getrennte Datei (*.csv)\nMessage.CheckRecurring               = Pr\\u00FCfe auf wiederkehrende Ereignisse\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Sammle Daten f\\u00FCr den Bericht\nMessage.CompilingReport              = Stelle Bericht zusammen\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Ausgew\\u00E4hlte Buchungen l\\u00F6schen?\nMessage.ConfirmReminderDelete        = Ausgew\\u00E4hlte Erinnerung l\\u00F6schen?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Ausgew\\u00E4hlte Buchung l\\u00F6schen?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Standardw\\u00E4hrung ge\\u00E4ndert zu:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine gestartet\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Konto kann nicht erzeugt werden\nMessage.Error.AccountRemove          = Konto konnte nicht gel\\u00F6scht werden\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Anlage konnte nicht eingef\\u00FCgt werden\nMessage.Error.AddCurrency            = W\\u00E4hrung konnte nicht eingef\\u00FCgt werden\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Bitte richten Sie zuerst die Basiskonten einrichten\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplikate sind nicht erlaubt\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = Datei nicht gefunden\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = Die Lizenz wurde nicht akzeptiert\nMessage.Error.LoadingFile            = Fehler beim Laden der Datei\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Anlage konnte nicht ge\\u00E4ndert werden\nMessage.Error.ModifyCurrency         = W\\u00E4hrung konnte nicht ge\\u00E4ndert werden\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Keine Buchungen gefunden\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = Die Verbindung zum Server ist fehlgeschlagen\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Datei geschlossen\nMessage.FileIsLocked                 = Die Datei ist von einer anderen Applikation gesperrt\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = Die gew\\u00E4hlte Datei ist ung\\u00FCltig\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Bitte warten, der Import kann lange dauern\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash benu00F6tigt Java 11 oder hu00F6her\nMessage.LoadReportFail               = Berichtsdefinition konnte nicht geladen werden\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Regionale Einstellungen ge\\u00E4ndert zu:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Keine Wiederholungen\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = Die vorhandene Datenbank wird \\u00FCberschrieben\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Fehler beim Einlesen der Berichtsdefinition\nMessage.PleaseWait                   = Bitte warten\nMessage.PrefFail                     = Konnte alte Einstellungen nicht finden\nMessage.PrepShutdown                 = Beenden wird vorbereitet\nMessage.ProcessingReportData         = Verarbeite Daten f\\u00FCr den Bericht\nMessage.Proxy                        = Setze HTTP Proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Berichtsmodule wurden erfolgreich geladen\nMessage.ReportWait                   = Bitte warten, Berichtsmodule werden geladen\nMessage.RestartLocale                = Neustart erforderlich, damit Regionale Einstellungen wirksam werden\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Suche\\u2026 Bitte warten\nMessage.Shutdown                     = Beenden\nMessage.StartEndDate                 = Beginn- und Enddatum eingeben\nMessage.StoreBackup                  = Speichere Backup nach:\nMessage.StoreComplete                = Speichern der Datei abgeschlossen\nMessage.StoreWait                    = Warte auf Ende des Speichervorgangs\nMessage.TXTFile                      = Textdateien (*.txt)\nMessage.TransactionAccountLocked     = Transaktion einf\\u00FCgen fehlgeschlagen. Zielkonto(en) gesperrt\nMessage.TransactionAdd               = Transaktion eingef\\u00FCgt\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transaktion entfernt\nMessage.TransactionRemoveLocked      = Transaktion entfernen fehlgeschlagen. Zielkonto(en) gesperrt\nMessage.UninstallBad                 = Deinstallation wurde nicht erfolgreich abgeschlossen\nMessage.UninstallGood                = Deeinstallation erfolgreich!\nMessage.UpdatedPrice                 = Preis f\\u00FCr {0} aktualisiert\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = Sie verwenden Version\nMessage.Warn.CommodityInUse          = Anlage wird bereits verwendet\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = W\\u00E4hrung wird bereits verwendet\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\\n\\nDo you want to continue?\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bankkonten\nName.ExpenseAccounts = Ausgaben\nName.IncomeAccounts  = Einnahmen\nName.Root            = Wurzel\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Von {0,date,long} bis {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Seite {0} von {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minuten\nPeriod.15Min     = 15 Minuten\nPeriod.1Day      = 1 Tag\nPeriod.1Hr       = 1 Stunde\nPeriod.2Hr       = 2 Stunden\nPeriod.30Min     = 30 Minuten\nPeriod.5Min      = 5 Minuten\nPeriod.8Hr       = 8 Stunden\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = T\\u00E4glich\nPeriod.Monthly   = Monatlich\nPeriod.NextStart = Beim n\\u00E4chsten Start von jGnash\nPeriod.None      = Keine\nPeriod.OnlyOnce  = Einmalig\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = W\\u00F6chentlich\nPeriod.Yearly    = J\\u00E4hrlich\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = Keine\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! GB und Irland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = Z\nState.NotReconciled = \\u200B\nState.Reconciled    = A\n\nTab.About           = Info\nTab.Accounts        = Accounts\nTab.Adjust          = Anpassen\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Belastung\nTab.Credit          = Haben\nTab.Credits         = Danksagungen\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = Tag\nTab.Debit           = Soll\nTab.Formats         = Formats\nTab.GPLLicense      = GPL Lizenz\nTab.General         = Allgemein\nTab.LGPLLicense     = LGPL Lizenz\nTab.License         = Lizenz\nTab.Month           = Monat\nTab.Network         = Network\nTab.None            = Nie\nTab.Payment         = Zahlung\nTab.Register        = Register\nTab.Reminders       = Erinnerungen\nTab.Report          = Bericht\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = System-Information\nTab.Transfer        = \\u00DCberweisung\nTab.Week            = Woche\nTab.Year            = Jahr\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = \\u00DCber\nTitle.AccountBalance             = Konten-Saldo\nTitle.AccountFilter              = Kontenfilter\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Konto-Information\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Wertpapier-Depot\nTitle.AddRemCurr                 = W\\u00E4hrungen hinzuf\\u00FCgen / entfernen\nTitle.AmortizationSetup          = Tilgung einrichten\nTitle.Archive                    = Archiv\nTitle.AutoComplete               = Autovervollst\\u00E4ndigung\nTitle.AutoSave                   = Automatisches Speichern\nTitle.Available                  = Verf\\u00FCgbar\nTitle.BackgroundUpdate           = Hintergrund-Updates\nTitle.BackingStore               = Sicherung speichern\nTitle.BalanceSheet               = Bilanzaufstellung\nTitle.BaseColor                  = Grundfarben \\u00E4ndern\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Passwort \\u00E4ndern\nTitle.CheckDesign                = Scheck Designer\nTitle.ChooseAccounts             = Zu erstellende Konten ausw\\u00E4hlen\nTitle.ColVis                     = Spalte sichtbar\nTitle.Colors                     = Farben\nTitle.CommoditiesSecurities      = Wertpapiere\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Best\\u00E4tigen\nTitle.ConnectServer              = Mit Server Verbinden\nTitle.Connection                 = Verbindung\nTitle.Console                    = Konsolen-Fenster\nTitle.CreateModifyCommodities    = Anlage erzeugen / \\u00E4ndern\nTitle.Credits                    = Haben\nTitle.Currencies                 = W\\u00E4hrungen\nTitle.Current                    = Aktuell\nTitle.CutOffDate                 = Stichtag ausw\\u00E4hlen\nTitle.DatabaseCfg                = Datenbank-Konfiguration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Soll\nTitle.DefDefCurr                 = Standardw\\u00E4hrung definieren\nTitle.DefTranNum                 = Standard Transaktions-Nummern\nTitle.DefaultBehavior            = Standardverhalten\nTitle.Defaults                   = Standards\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Anzeigen\nTitle.DuplicateTransaction       = Duplizierte Buchung\nTitle.DuplicateTransactionsFound = Buchungs-Duplikate gefunden\nTitle.EditExchangeRates          = Wechselkurse bearbeiten\nTitle.EndMonthBalance            = Kontosaldo Monatsende\nTitle.EnterPassword              = Passwort eingeben\nTitle.Entry                      = Eintrag\nTitle.Error                      = Fehler\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Wechselkurs\nTitle.FileImport                 = Datei f\\u00FCr Import ausw\\u00E4hlen\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filter\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Standard-Schriftarten\nTitle.Frequency                  = H\\u00E4ufigkeit\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historie importieren\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = QIF-Datei teilweise importieren\nTitle.ImpSum                     = Zusammenfassung importieren\nTitle.ImportOFX                  = OFX-Datei importieren\nTitle.ImportTransactions         = Transaktionen aus einer Datei importieren\nTitle.IncomeExpenseBarChart      = Einnahmen und Ausgaben Balkendiagramm\nTitle.IncomeExpenseChart         = Einnahmen und Ausgaben Tortendiagramm\nTitle.Information                = Information\nTitle.InvFees                    = Investitionsgeb\\u00FChren\nTitle.InvGainsLoss               = Investition Gewinn / Verlust\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Transaktionen \\u00E4ndern\nTitle.ModOFXTrans                = OFX Transaktionen \\u00E4ndern\nTitle.ModQIFTrans                = QIF Transaktionen \\u00E4ndern\nTitle.ModifyAccount              = Konto bearbeiten\nTitle.ModifyCurrencies           = W\\u00E4hrungen bearbeiten\nTitle.ModifyReminder             = Erinnerung bearbeiten\nTitle.ModifySecHistory           = Wertpapierhistorie bearbeiten\nTitle.ModifyTransaction          = Transaktion bearbeiten\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Neues Konto\nTitle.NewBudget                  = New Budget\nTitle.NewFile                    = Neue Datei\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Neue Erinnerung\nTitle.NewTrans                   = Neue Transaktion\nTitle.Notes                      = Notizen\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = \\u00D6ffnen\nTitle.Options                    = Optionen\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Seite einrichten\nTitle.ParentAccount              = Oberkonto\nTitle.PercentDist                = Prozent Verteilung\nTitle.PercentExpense             = Prozent Ausgaben\nTitle.PercentIncome              = Prozent Einnahmen\nTitle.PleaseWait                 = Bitte warten\nTitle.PortfolioReport            = Portfolio Bericht\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Gewinn und Verlust-Aufstellung\nTitle.ReconcileSettings          = Abgleich-Einstellungen\nTitle.Reminder                   = Erinnerung\nTitle.Reminders                  = Erinnerungen\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Speichern unter\nTitle.SaveFile                   = Datei speichern\nTitle.SecurityHistory            = Wertpapier-Historie\nTitle.SelAccount                 = Konto ausw\\u00E4hlen\nTitle.SelAvailCurr               = Verf\\u00FCgbare W\\u00E4hrungen ausw\\u00E4hlen\nTitle.SelDate                    = Datum ausw\\u00E4hlen\nTitle.SelDefCurr                 = Standard-W\\u00E4hrung ausw\\u00E4hlen\nTitle.SelDefLocale               = Standard-Regionaleinstellung ausw\\u00E4hlen\nTitle.SelDestAccount             = Zielkonto ausw\\u00E4hlen\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Eigenkaptialkonto ausw\\u00E4hlen\nTitle.SelFile                    = Datei ausw\\u00E4hlen\nTitle.SelQifDateFormat           = QIF Datumsformat ausw\\u00E4hlen\nTitle.SelectColor                = Farbe ausw\\u00E4hlen\nTitle.Selected                   = Ausgew\\u00E4hlt\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaktion\nTitle.Startup                    = Startup\nTitle.Steps                      = Schritte\nTitle.Success                    = Erfolg\nTitle.Summary                    = Zusammenfassung\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaktion\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Transaktions-Liste\nTitle.TransactionSetup           = Transaktion einrichten\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Sichtbar\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Kontenliste\nToolTip.AccountRegister                      = Kontenregister\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Spalten-Sichtbarkeit \\u00E4ndern\nToolTip.ConvertSEntry                        = Konvertiert diese Buchung in doppelte Buchf\\u00FChrung\nToolTip.DeleteAccount                        = Konto l\\u00F6schen\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Konten filtern\nToolTip.FilterAccounts                       = Filtern nach Konto-Typ\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Schriftgr\\u00F6sse \\u00E4ndern\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = nur ganze Zahlen erlaubt\nToolTip.ModifyAccount                        = Konto bearbeiten\nToolTip.NewAccount                           = Neues Konto\nToolTip.PageSetup                            = Seite einrichten\nToolTip.PrintRegRep                          = Register-Bericht drucken\nToolTip.ReconcileAccount                     = Konto abgleichen\nToolTip.Reminders                            = Erinnerungen\nToolTip.ResizeColumns                        = Spaltenbreite \\u00E4ndern\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Anzahl Ziffern nach dem Komma\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Sicherung mit Zeitstempel beim Schlie\\u00DFen\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = \\u00D6ffnet ein neues Kontenregister\n\nTransaction.AddShare        = Anteile hinzuf\\u00FCgen\nTransaction.BuyShare        = Anteile kaufen\nTransaction.Dividend        = Dividende\nTransaction.DoubleEntry     = Doppelte Buchf\\u00FChrung\nTransaction.MergeShare      = Aktien-Fusion\nTransaction.ReinvestDiv     = Dividene Re-Investition\nTransaction.RemoveShare     = Anteil entfernen\nTransaction.ReturnOfCapital = Kapitalrendite\nTransaction.SellShare       = Anteil verkaufen\nTransaction.SingleEntry     = Einfache Buchf\\u00FChrung\nTransaction.Split           = Split-Buchung\nTransaction.SplitEntry      = Split-Buchungsteil\nTransaction.SplitShare      = Aktien-Split\nTransaction.TransferIn      = \\u00DCberweisung Eingehend\nTransaction.TransferOut     = \\u00DCberweisung Ausgehend\n\nWord.Add             = Hinzuf\\u00FCgen\nWord.All             = Alle\nWord.Balance         = Saldo\nWord.Buy             = Kaufen\nWord.Commodity       = Anlage\nWord.Copy            = Copy\nWord.Description     = Beschreibung\nWord.Difference      = Unterschied\nWord.Dividend        = Dividende\nWord.Exchange        = Wechselkurs\nWord.Fees            = Geb\\u00FChren\nWord.GrossExpense    = Brutto-Ausgaben\nWord.GrossIncome     = Brutto-Einnahmen\nWord.Interest        = Zinsen\nWord.Into            = in\nWord.Invalid         = ung\\u00FCltig\nWord.Merge           = gehen\nWord.Monthly         = Monatlich\nWord.Name            = Name\nWord.NetIncome       = Netto-Einnahmen\nWord.NetWorth        = Nettowert\nWord.NewBudget       = New Budget\nWord.None            = Keine\nWord.Quarterly       = Quartalsweise\nWord.ReInvDiv        = Dividende reinvestieren\nWord.Remove          = Entfernen\nWord.ReturnOfCapital = Kapitalrendite\nWord.Seconds         = seconds\nWord.Security        = Wertpapier\nWord.Sell            = Verkaufen\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Summe\nWord.Totals          = Totals\nWord.Yearly          = J\\u00E4hrlich\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_en.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Asset\nAccountType.Bank             = Bank\nAccountType.Cash             = Cash\nAccountType.Checking         = Checking\nAccountType.Credit           = Credit\nAccountType.Equity           = Equity\nAccountType.Expense          = Expense\nAccountType.Income           = Income\nAccountType.Investment       = Investment\nAccountType.Liability        = Liability\nAccountType.MoneyMarket      = Money Market\nAccountType.Mutual           = Mutual Fund\nAccountType.Root             = Root\nAccountType.SimpleInvestment = Simple Investment\n\nButton.AccTerms                = Use formal accounting terms\nButton.Accounts                = Accounts\nButton.AckSel                  = Acknowledge Selected\nButton.Add                     = Add\nButton.AllDates                = All Dates\nButton.Amortize                = Amortize\nButton.AnimationsEnabled       = Enable animation effects\nButton.AnyStatus               = Any Status\nButton.Apply                   = Apply\nButton.AssetAccounts           = Asset Accounts\nButton.AutoReconcile           = Automatically Reconcile Split Transactions\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = Back\nButton.BankAccounts            = Bank Accounts\nButton.BudgetMgr               = Budget Manager\nButton.Budgeting               = Budgeting\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Cancel\nButton.CheckForUpdates         = Check for new updates\nButton.CheckReminders          = Check Reminders\nButton.Clear                   = Clear\nButton.ClearAll                = Clear All\nButton.Cleared                 = Cleared\nButton.Close                   = Close\nButton.Compare                 = Compare\nButton.ConcatenateMemos        = Concatenate Memos\nButton.ConfirmReminderDelete   = Confirm on reminder delete\nButton.ConfirmTransDelete      = Confirm on transaction delete\nButton.CopyToClip              = Copy To Clipboard\nButton.CreateTimeFile          = Create timestamped file on exit\nButton.CreditAccounts          = Credit Accounts\nButton.Delete                  = Delete\nButton.DeleteAll               = Delete All\nButton.DeleteWeekends          = Delete Weekends\nButton.DetailSplits            = Show Split Details\nButton.Duplicate               = Duplicate\nButton.Edit                    = Edit\nButton.EnableAutoComplete      = Enable auto completion\nButton.Enabled                 = Enabled\nButton.EndingBalance           = Ending Balance\nButton.Enter                   = Enter\nButton.EnterDaysBefore         = Automatically enter number of days before\nButton.ExcludeFromBudget       = Exclude From Budgets\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Expense Accounts\nButton.Export                  = Export\nButton.ExportSpreadsheet       = Export Spreadsheet\nButton.Filter                  = Filter\nButton.Finish                  = Finish\nButton.FinishLater             = Finish Later\nButton.ForceDefaultCurrency    = Force use of Default Currency\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = Requires Authentication\nButton.Hidden                  = Hidden\nButton.HideAccount             = Hide Account\nButton.HideLockedAccount       = Hide locked accounts\nButton.HidePlaceholderAccount  = Hide placeholder accounts\nButton.HideZeroBalance         = Hide zero balance accounts\nButton.HistoricalFill          = Historical Fill\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Include Sub Accounts\nButton.IncomeAccounts          = Income Accounts\nButton.IncomeAndExpense        = Income and Expense\nButton.Insert                  = Insert\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Invert Selection\nButton.Jump                    = Jump\nButton.KeepFridays             = Keep Fridays\nButton.KeepMondays             = Keep Mondays\nButton.Landscape               = Landscape\nButton.Last120Days             = Last 120 Days\nButton.Last12Months            = Last 12 Months\nButton.Last30Days              = Last 30 Days\nButton.Last60Days              = Last 60 Days\nButton.Last90Days              = Last 90 Days\nButton.LiabilityAccounts       = Liability Accounts\nButton.Locked                  = Locked\nButton.MatchAccountOnly        = Match using only account specific transactions\nButton.MatchAllTrans           = Match using all transactions\nButton.MatchCaseSensitive      = Match is case sensitive\nButton.Modify                  = Modify\nButton.MonthlyBalance          = Monthly Balance\nButton.New                     = New\nButton.NewEmpty                = New Empty\nButton.NewHist                 = New Historical\nButton.NewPayment              = New Payment\nButton.Next                    = Next\nButton.No                      = No\nButton.NoEndDate               = No End Date\nButton.None                    = None\nButton.NotReconciled           = Not Reconciled\nButton.Ok                      = OK\nButton.Open                    = Open\nButton.OpenLastOnStartup       = Open last file on startup\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Page Setup\nButton.PlaceHolder             = Placeholder\nButton.Portrait                = Portrait\nButton.Print                   = Print\nButton.PrintSample             = Print sample\nButton.Properties              = Properties\nButton.Reconcile               = Reconcile\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disable automatic reconciliation\nButton.ReconcileIncomeExpense  = Automatically reconcile Income and Expense Accounts\nButton.Reconciled              = Reconciled\nButton.Refresh                 = Refresh\nButton.RegDate                 = Remember last transaction date\nButton.Register                = Register\nButton.RegisterFollowsList     = Register follows account list\nButton.RememberPassword        = Remember Password\nButton.RemindLater             = Remind Me Later\nButton.Reminders               = Reminders\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Remove\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Resize\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Running Balance\nButton.Save                    = Save\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Select\nButton.SelectAll               = Select All\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Show all Commodities\nButton.ShowEmptyAccounts       = Show Zero Balance Accounts\nButton.ShowPercentValues       = Show Percentages\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Splits\nButton.Start                   = Start\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Themes Enabled\nButton.Timestamp               = Timestamp\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Update currency exchange rates on startup\nButton.UpdateOnline            = Update Online\nButton.UpdateSecuritiesStartup = Update securities on startup\nButton.UseDailyRate            = Use Daily Periodic Rate\nButton.UseEncryption           = Use Encryption\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Use Long Names\nButton.UseProxy                = Use Proxy\nButton.UseRegexForFilter       = Use regular expressions for filters\nButton.Vertical                = Vertical\nButton.Yes                     = Yes\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Account\nColumn.AccountName                    = Account Name\nColumn.Action                         = Action\nColumn.Actual                         = Actual\nColumn.Amount                         = Amount\nColumn.Approve                        = Approve\nColumn.Balance                        = Balance\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Charge\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = Commodity\nColumn.CostBasis                      = CB\nColumn.Credit                         = Credit\nColumn.Currency                       = Currency\nColumn.Date                           = Date\nColumn.Day                            = Day\nColumn.Debit                          = Debit\nColumn.Decrease                       = Decrease\nColumn.Deposit                        = Deposit\nColumn.Description                    = Description\nColumn.Due                            = Due\nColumn.Enabled                        = Enabled\nColumn.Entries                        = Entries\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Exchange Rate\nColumn.Expense                        = Expense\nColumn.Freq                           = Frequency\nColumn.Gain                           = Gain\nColumn.High                           = High\nColumn.Income                         = Income\nColumn.Increase                       = Increase\nColumn.Investment                     = Investment\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Loss\nColumn.Low                            = Low\nColumn.Memo                           = Memo\nColumn.MktValue                       = Mkt Value\nColumn.Month                          = Month\nColumn.Num                            = Num\nColumn.Payee                          = Payee\nColumn.Payment                        = Payment\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Price\nColumn.Print                          = Print\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Quantity\nColumn.Rebate                         = Rebate\nColumn.Receive                        = Receive\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = Spend\nColumn.Timestamp                      = Timestamp\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Type\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Withdrawal\n\nDataStoreType.Bxds = Binary File\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Amount\nItem.CashDeposit    = Cash Deposit\nItem.CashWithdrawal = Cash Withdrawal\nItem.Date           = Date\nItem.EFT            = EFT\nItem.Memo           = Memo\nItem.NextNum        = Next #\nItem.Payee          = Payee\nItem.Trans          = Trans\n\nLabel.AccentColor         = Accent Color:\nLabel.Account             = Account:\nLabel.AccountCode         = Account Code:\nLabel.AccountNumber       = Account Number:\nLabel.AccountOptions      = Account Options:\nLabel.AccountSeparator    = Account Separator:\nLabel.AccountStatus       = Account Status:\nLabel.AccountType         = Account Type:\nLabel.Action              = Action:\nLabel.Amount              = Amount:\nLabel.AnIntRate           = Annual Interest Rate (APR):\nLabel.Available           = Available:\nLabel.Balance             = Balance:\nLabel.BankAccount         = Bank Account:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Base Account:\nLabel.BaseColor           = Base Color:\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = Cash Balance:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Commodity:\nLabel.CompDaysPerYear     = Compounding Days per Year:\nLabel.CompPerTerm         = Compounding Periods per Year:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Count:\nLabel.CreateCurr          = Create Custom Currency:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Currency / Commodity:\nLabel.Currencies          = Currencies:\nLabel.Currency            = Currency:\nLabel.Current             = Current:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Date:\nLabel.DateFormat          = Date Format:\nLabel.DaysPastDue         = Days past due:\nLabel.DefaultCurrency     = Default Currency:\nLabel.DelaySec            = Delay (seconds)\nLabel.Description         = Description:\nLabel.DestAccount         = Destination Account:\nLabel.Difference          = Difference:\nLabel.Dividend            = Dividend:\nLabel.EndDate             = End Date:\nLabel.EndOn               = End On:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Ending Balance:\nLabel.EquityAccount       = Equity Account:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Every:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Exchange Rate:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Fees:\nLabel.FeesAccount         = Fees Account:\nLabel.FileName            = File Name:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Date of first payment:\nLabel.FocusColor          = Focus Color:\nLabel.Format              = Format:\nLabel.Frequency           = Frequency:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gains:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Height:\nLabel.High                = High:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = CUSIP / ISIN:\nLabel.IncomeAccount       = Income Account:\nLabel.InterestAccount     = Interest Account:\nLabel.LastOccurrence      = Last Occurrence:\nLabel.Layout              = Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = Length of Loan (Months):\nLabel.Low                 = Low:\nLabel.MarketValue         = Market Value:\nLabel.MaxBackupCount      = Maximum number of backup files to keep:\nLabel.Memo                = Memo:\nLabel.Monospace           = Monospace:\nLabel.Name                = Name:\nLabel.NetIncome           = Net Income:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Next payment date:\nLabel.Notes               = Notes:\nLabel.NumTrans            = Number of Transactions:\nLabel.Number              = Number:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date:\nLabel.OpeningBalance      = Opening Balance:\nLabel.OrigLoanAmt         = Original Loan Amount:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Password:\nLabel.Path                = Path\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Payments per Year:\nLabel.Payee               = Payee:\nLabel.Period              = Period:\nLabel.Port                = Port:\nLabel.Prefix              = Prefix:\nLabel.Price               = Price:\nLabel.Proportional        = Proportional:\nLabel.Quantity            = Quantity:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Reconciled Balance:\nLabel.RemindLater         = Remind me again after:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Repeat on:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = Resolution:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Scale:\nLabel.Securities          = Securities:\nLabel.Security            = Security:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Show Empty Accounts\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Start Date:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Start Position:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Statement Date:\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = Suffix:\nLabel.Symbol              = Symbol:\nLabel.TargetBalance       = Target Balance:\nLabel.Top                 = Top:\nLabel.Total               = Total:\nLabel.Transaction         = Transaction:\nLabel.TransferFrom        = Transfer From:\nLabel.TransferTo          = Transfer To:\nLabel.Type                = Type:\nLabel.Units               = Units:\nLabel.UserName            = User Name:\nLabel.Value               = Value:\nLabel.Verify              = Verify:\nLabel.VerifyPassword      = Verify Password:\nLabel.Visible             = Visible:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = _About\\u2026\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = Account\nMenu.AccountRegister.Name             = Account Register\\u2026\nMenu.AddRemoveCurrency.Name=_Add / Remove\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Update Currencies\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = Update Securities\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet\\u2026\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = _Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Cleared\nMenu.Close.Name                       = Close\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters\\u2026\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = C_opy\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = C_urrencies\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Change _default\\u2026\nMenu.DefaultCurrency.Tooltip          = Change the default currency\nMenu.Delete.Name                      = Delete\nMenu.Duplicate.Name                   = Duplicate\nMenu.Edit.Name                        = _Edit\nMenu.EditTranNumList.Name             = Edit Transaction Number List\\u2026\nMenu.Exit.Name                        = _Quit\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = _Export\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archive\\u2026\nMenu.File.Name                        = _File\nMenu.Filter.Name                      = _Filters\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Help\nMenu.Hide.Name                        = Hide\nMenu.HistoryChart.Name                = Historical Chart\\u2026\nMenu.HistoryCommodity.Name            = _History\\u2026\nMenu.HistoryImport.Name               = Historical Import\\u2026\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Income / Expense Pie Chart\\u2026\nMenu.Import.Name                      = _Import\nMenu.ImportAccounts.Name              = Import Accounts\\u2026\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Import jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = List of Accounts\\u2026\nMenu.Locale.Name                      = Change Locale\\u2026\nMenu.LookAndFeel.Name                 = Look and Feel\nMenu.MarkAs.Name                      = Mark As\nMenu.Modify.Name                      = Modify\nMenu.ModifyCommodity.Name             = _Create / Modify\\u2026\nMenu.ModifyCurrency.Name              = _Modify\\u2026\nMenu.ModifyExchangeRates.Name         = _Edit Exchange Rates\\u2026\nMenu.MonthBalance.Name                = Monthly Balance\\u2026\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = End-Of-Month Balance\\u2026\nMenu.MonthEndBalanceCSV.Name          = End-Of-Month Balance (CSV)\\u2026\nMenu.NetWorth.Name                    = Net Worth\\u2026\nMenu.New.Name                         = _New\\u2026\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Open\\u2026\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = _Options\\u2026\nMenu.Option.Tooltip                   = Change program options\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = Periodic Account Balance\\u2026\nMenu.Portfolio.Name                   = Portfolio\\u2026\nMenu.ProfitLoss.Name                  = Profit and Loss\\u2026\nMenu.ProfitLossTXT.Name               = Profit and Loss (Text)\\u2026\nMenu.Reconcile.Name                   = Reconcile\nMenu.Reconciled.Name                  = Reconciled\nMenu.RecurringList.Name               = Recurring Transactions\\u2026\nMenu.Register.Name                    = _Register\\u2026\nMenu.Reports.Name                     = _Reports\nMenu.RunJavaScript.Name               = Run JavaScript\\u2026\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = _Save\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = Save _As\\u2026\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = _Securities\nMenu.SetPassword.Name                 = Set Password\\u2026\nMenu.Show.Name                        = Show\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Themes\nMenu.Tools.Name                       = _Tools\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Unreconciled\nMenu.View.Name                        = _View\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = I have read, understand, and accept the terms of the licenses.\nMessage.AccountAdd                   = Account added\nMessage.AccountCode                  = Account code was not unique: The code was not changed\nMessage.AccountLocked                = Account is Locked\nMessage.AccountModify                = Account modified\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = Account removed\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = AutoSave has been turned off\nMessage.AutoSaveOn                   = AutoSave has been turned on\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Checking for new recurring events\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\\u2026\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Delete the selected transactions?\nMessage.ConfirmReminderDelete        = Delete the selected reminder?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Delete the selected transaction?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Default currency changed to:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine started\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Unable to create account\nMessage.Error.AccountRemove          = Could not remove account\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Could not add commodity\nMessage.Error.AddCurrency            = Could not add currency\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Create a basic account set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = File was not found\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = The license was not accepted\nMessage.Error.LoadingFile            = An error occurred when loading the file\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Could not modify commodity\nMessage.Error.ModifyCurrency         = Could not modify currency\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = File closed\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Default locale changed to:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = Preparing for shutdown\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Report modules were loaded successfully\nMessage.ReportWait                   = Please wait, Loading report modules\nMessage.RestartLocale                = Restart for locale change to take effect\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = Enter starting and ending dates\nMessage.StoreBackup                  = Saving backup to:\nMessage.StoreComplete                = File store is complete\nMessage.StoreWait                    = Waiting for file save to complete\nMessage.TXTFile                      = Text Files (*.txt)\nMessage.TransactionAccountLocked     = Transaction add failed, Destination account(s) are locked\nMessage.TransactionAdd               = Transaction added\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transaction removed\nMessage.TransactionRemoveLocked      = Transaction removal failed, Destination account(s) are locked\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = Commodity is in use\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Currency is in use\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\\n\\nDo you want to continue?\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bank Accounts\nName.ExpenseAccounts = Expense Accounts\nName.IncomeAccounts  = Income Accounts\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = From {0,date,long} To {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Page {0} of {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minutes\nPeriod.15Min     = 15 Minutes\nPeriod.1Day      = 1 Day\nPeriod.1Hr       = 1 Hour\nPeriod.2Hr       = 2 Hours\nPeriod.30Min     = 30 Minutes\nPeriod.5Min      = 5 Minutes\nPeriod.8Hr       = 8 Hours\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Daily\nPeriod.Monthly   = Monthly\nPeriod.NextStart = Next time jGnash starts\nPeriod.None      = None\nPeriod.OnlyOnce  = Only once\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Weekly\nPeriod.Yearly    = Yearly\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = About\nTab.Accounts        = Accounts\nTab.Adjust          = Adjust\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Charge\nTab.Credit          = Credit\nTab.Credits         = Credits\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = Day\nTab.Debit           = Debit\nTab.Formats         = Formats\nTab.GPLLicense      = GPL License\nTab.General         = General\nTab.LGPLLicense     = LGPL License\nTab.License         = License\nTab.Month           = Month\nTab.Network         = Network\nTab.None            = None\nTab.Payment         = Payment\nTab.Register        = Register\nTab.Reminders       = Reminders\nTab.Report          = Report\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = System Information\nTab.Transfer        = Transfer\nTab.Week            = Week\nTab.Year            = Year\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = About\nTitle.AccountBalance             = Account Balance\nTitle.AccountFilter              = Account Filters\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Account Information\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Account Securities\nTitle.AddRemCurr                 = Add / Remove Currencies\nTitle.AmortizationSetup          = Amortization Setup\nTitle.Archive                    = Archive\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Automatic Save\nTitle.Available                  = Available\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Backing Store\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Check Designer\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Column Visibility\nTitle.Colors                     = Colors\nTitle.CommoditiesSecurities      = Securities\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirm\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Create / Modify Securities\nTitle.Credits                    = Credits\nTitle.Currencies                 = Currencies\nTitle.Current                    = Current\nTitle.CutOffDate                 = Select Cut Off Date\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Debits\nTitle.DefDefCurr                 = Define Default Currency\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Display\nTitle.DuplicateTransaction       = Duplicate Transaction\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = End-Of-Month Account Balance\nTitle.EnterPassword              = Enter Password\nTitle.Entry                      = Entry\nTitle.Error                      = Error\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Exchange Rate\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filters\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Frequency\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historical Import\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Import a partial QIF file\nTitle.ImpSum                     = Import Summary\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = Information\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Modify QIF Transactions\nTitle.ModifyAccount              = Modify Account\nTitle.ModifyCurrencies           = Modify Currencies\nTitle.ModifyReminder             = Modify Reminder\nTitle.ModifySecHistory           = Modify Securities History\nTitle.ModifyTransaction          = Modify Transaction\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = New Account\nTitle.NewBudget                  = New Budget\nTitle.NewFile                    = Create a New File\nTitle.NewPassword                = New Password\nTitle.NewReminder                = New Reminder\nTitle.NewTrans                   = New Transaction\nTitle.Notes                      = Notes\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Open\nTitle.Options                    = Options\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Page Setup\nTitle.ParentAccount              = Parent Account\nTitle.PercentDist                = Percent Distribution\nTitle.PercentExpense             = Percent Expense\nTitle.PercentIncome              = Percent Income\nTitle.PleaseWait                 = Please Wait\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profit and Loss Statement\nTitle.ReconcileSettings          = Reconcile Settings\nTitle.Reminder                   = Reminder\nTitle.Reminders                  = Reminders\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Save As\nTitle.SaveFile                   = Save File\nTitle.SecurityHistory            = Security History\nTitle.SelAccount                 = Select Account\nTitle.SelAvailCurr               = Select Available Currencies\nTitle.SelDate                    = Select a Date\nTitle.SelDefCurr                 = Select Default Currency\nTitle.SelDefLocale               = Select Default Locale\nTitle.SelDestAccount             = Select Destination Account\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Select Equity Account\nTitle.SelFile                    = Select File\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = Select Color\nTitle.Selected                   = Selected\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaction\nTitle.Startup                    = Startup\nTitle.Steps                      = Steps\nTitle.Success                    = Success\nTitle.Summary                    = Summary\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaction\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Transaction List\nTitle.TransactionSetup           = Transaction Setup\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visible\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Account list\nToolTip.AccountRegister                      = Account register\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Change column visibility\nToolTip.ConvertSEntry                        = Change this transaction into a Double Entry transaction\nToolTip.DeleteAccount                        = Delete Account\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filter accounts\nToolTip.FilterAccounts                       = Filter by account type\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = Only integers allowed\nToolTip.ModifyAccount                        = Modify account\nToolTip.NewAccount                           = New Account\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = Print register report\nToolTip.ReconcileAccount                     = Reconcile Account\nToolTip.Reminders                            = Reminders\nToolTip.ResizeColumns                        = Resize Columns\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Number of digits to the right of the decimal\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Create a backup with timestamp when closed\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Open a new account register\n\nTransaction.AddShare        = Add Shares (Adjust)\nTransaction.BuyShare        = Buy Shares\nTransaction.Dividend        = Dividend\nTransaction.DoubleEntry     = Double Entry\nTransaction.MergeShare      = Stock Merge\nTransaction.ReinvestDiv     = Reinvest Dividend\nTransaction.RemoveShare     = Remove Shares (Adjust)\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Sell Shares\nTransaction.SingleEntry     = Single Entry\nTransaction.Split           = Split Transaction\nTransaction.SplitEntry      = Split Transaction Entry\nTransaction.SplitShare      = Stock Split\nTransaction.TransferIn      = Transfer Cash In\nTransaction.TransferOut     = Transfer Cash Out\n\nWord.Add             = Add\nWord.All             = All\nWord.Balance         = Balance\nWord.Buy             = Buy\nWord.Commodity       = Commodity\nWord.Copy            = Copy\nWord.Description     = Description\nWord.Difference      = Difference\nWord.Dividend        = Dividend\nWord.Exchange        = Exchange\nWord.Fees            = Fees\nWord.GrossExpense    = Gross Expense\nWord.GrossIncome     = Gross Income\nWord.Interest        = Interest\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = Merge\nWord.Monthly         = By month\nWord.Name            = Name\nWord.NetIncome       = Net Income\nWord.NetWorth        = Net Worth\nWord.NewBudget       = New Budget\nWord.None            = None\nWord.Quarterly       = Quarterly\nWord.ReInvDiv        = Reinvest Dividend\nWord.Remove          = Remove\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Security\nWord.Sell            = Sell\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totals\nWord.Yearly          = Yearly\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_en_GB.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Asset\nAccountType.Bank             = Bank\nAccountType.Cash             = Cash\nAccountType.Checking         = Checking\nAccountType.Credit           = Credit\nAccountType.Equity           = Equity\nAccountType.Expense          = Expense\nAccountType.Income           = Income\nAccountType.Investment       = Investment\nAccountType.Liability        = Liability\nAccountType.MoneyMarket      = Money Market\nAccountType.Mutual           = Mutual Fund\nAccountType.Root             = Root\nAccountType.SimpleInvestment = Simple Investment\n\nButton.AccTerms                = Use formal accounting terms\nButton.Accounts                = Accounts\nButton.AckSel                  = Acknowledge Selected\nButton.Add                     = Add\nButton.AllDates                = All Dates\nButton.Amortize                = Amortize\nButton.AnimationsEnabled       = Enable animation effects\nButton.AnyStatus               = Any Status\nButton.Apply                   = Apply\nButton.AssetAccounts           = Asset Accounts\nButton.AutoReconcile           = Automatically Reconcile Split Transactions\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = Back\nButton.BankAccounts            = Bank Accounts\nButton.BudgetMgr               = Budget Manager\nButton.Budgeting               = Budgeting\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Cancel\nButton.CheckForUpdates         = Check for new updates\nButton.CheckReminders          = Check Reminders\nButton.Clear                   = Clear\nButton.ClearAll                = Clear All\nButton.Cleared                 = Cleared\nButton.Close                   = Close\nButton.Compare                 = Compare\nButton.ConcatenateMemos        = Concatenate Memos\nButton.ConfirmReminderDelete   = Confirm on reminder delete\nButton.ConfirmTransDelete      = Confirm on transaction delete\nButton.CopyToClip              = Copy To Clipboard\nButton.CreateTimeFile          = Create timestamped file on exit\nButton.CreditAccounts          = Credit Accounts\nButton.Delete                  = Delete\nButton.DeleteAll               = Delete All\nButton.DeleteWeekends          = Delete Weekends\nButton.DetailSplits            = Show Split Details\nButton.Duplicate               = Duplicate\nButton.Edit                    = Edit\nButton.EnableAutoComplete      = Enable auto completion\nButton.Enabled                 = Enabled\nButton.EndingBalance           = Ending Balance\nButton.Enter                   = Enter\nButton.EnterDaysBefore         = Automatically enter number of days before\nButton.ExcludeFromBudget       = Exclude From Budgets\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Expense Accounts\nButton.Export                  = Export\nButton.ExportSpreadsheet       = Export Spreadsheet\nButton.Filter                  = Filter\nButton.Finish                  = Finish\nButton.FinishLater             = Finish Later\nButton.ForceDefaultCurrency    = Force use of Default Currency\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = Requires Authentication\nButton.Hidden                  = Hidden\nButton.HideAccount             = Hide Account\nButton.HideLockedAccount       = Hide locked accounts\nButton.HidePlaceholderAccount  = Hide placeholder accounts\nButton.HideZeroBalance         = Hide zero balance accounts\nButton.HistoricalFill          = Historical Fill\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Include Sub Accounts\nButton.IncomeAccounts          = Income Accounts\nButton.IncomeAndExpense        = Income and Expense\nButton.Insert                  = Insert\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Invert Selection\nButton.Jump                    = Jump\nButton.KeepFridays             = Keep Fridays\nButton.KeepMondays             = Keep Mondays\nButton.Landscape               = Landscape\nButton.Last120Days             = Last 120 Days\nButton.Last12Months            = Last 12 Months\nButton.Last30Days              = Last 30 Days\nButton.Last60Days              = Last 60 Days\nButton.Last90Days              = Last 90 Days\nButton.LiabilityAccounts       = Liability Accounts\nButton.Locked                  = Locked\nButton.MatchAccountOnly        = Match using only account specific transactions\nButton.MatchAllTrans           = Match using all transactions\nButton.MatchCaseSensitive      = Match is case sensitive\nButton.Modify                  = Modify\nButton.MonthlyBalance          = Monthly Balance\nButton.New                     = New\nButton.NewEmpty                = New Empty\nButton.NewHist                 = New Historical\nButton.NewPayment              = New Payment\nButton.Next                    = Next\nButton.No                      = No\nButton.NoEndDate               = No End Date\nButton.None                    = None\nButton.NotReconciled           = Not Reconciled\nButton.Ok                      = OK\nButton.Open                    = Open\nButton.OpenLastOnStartup       = Open last file on startup\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Page Setup\nButton.PlaceHolder             = Placeholder\nButton.Portrait                = Portrait\nButton.Print                   = Print\nButton.PrintSample             = Print sample\nButton.Properties              = Properties\nButton.Reconcile               = Reconcile\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disable automatic reconciliation\nButton.ReconcileIncomeExpense  = Automatically reconcile Income and Expense Accounts\nButton.Reconciled              = Reconciled\nButton.Refresh                 = Refresh\nButton.RegDate                 = Remember last transaction date\nButton.Register                = Register\nButton.RegisterFollowsList     = Register follows account list\nButton.RememberPassword        = Remember Password\nButton.RemindLater             = Remind Me Later\nButton.Reminders               = Reminders\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Remove\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Resize\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Running Balance\nButton.Save                    = Save\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Select\nButton.SelectAll               = Select All\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Show all Commodities\nButton.ShowEmptyAccounts       = Show Zero Balance Accounts\nButton.ShowPercentValues       = Show Percentages\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Splits\nButton.Start                   = Start\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Themes Enabled\nButton.Timestamp               = Timestamp\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Update currency exchange rates on startup\nButton.UpdateOnline            = Update Online\nButton.UpdateSecuritiesStartup = Update securities on startup\nButton.UseDailyRate            = Use Daily Periodic Rate\nButton.UseEncryption           = Use Encryption\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Use Long Names\nButton.UseProxy                = Use Proxy\nButton.UseRegexForFilter       = Use regular expressions for filters\nButton.Vertical                = Vertical\nButton.Yes                     = Yes\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Account\nColumn.AccountName                    = Account Name\nColumn.Action                         = Action\nColumn.Actual                         = Actual\nColumn.Amount                         = Amount\nColumn.Approve                        = Approve\nColumn.Balance                        = Balance\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Charge\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = Commodity\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Credit\nColumn.Currency                       = Currency\nColumn.Date                           = Date\nColumn.Day                            = Day\nColumn.Debit                          = Debit\nColumn.Decrease                       = Decrease\nColumn.Deposit                        = Deposit\nColumn.Description                    = Description\nColumn.Due                            = Due\nColumn.Enabled                        = Enabled\nColumn.Entries                        = Entries\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Exchange Rate\nColumn.Expense                        = Expense\nColumn.Freq                           = Frequency\nColumn.Gain                           = Gain\nColumn.High                           = High\nColumn.Income                         = Income\nColumn.Increase                       = Increase\nColumn.Investment                     = Investment\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Loss\nColumn.Low                            = Low\nColumn.Memo                           = Memo\nColumn.MktValue                       = Mkt Value\nColumn.Month                          = Month\nColumn.Num                            = Num\nColumn.Payee                          = Payee\nColumn.Payment                        = Payment\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Price\nColumn.Print                          = Print\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Quantity\nColumn.Rebate                         = Rebate\nColumn.Receive                        = Receive\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = Spend\nColumn.Timestamp                      = Timestamp\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Type\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Withdrawal\n\nDataStoreType.Bxds = Binary File\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Amount\nItem.CashDeposit    = Cash Deposit\nItem.CashWithdrawal = Cash Withdrawal\nItem.Date           = Date\nItem.EFT            = EFT\nItem.Memo           = Memo\nItem.NextNum        = Next #\nItem.Payee          = Payee\nItem.Trans          = Trans\n\nLabel.AccentColor         = Accent Colour:\nLabel.Account             = Account:\nLabel.AccountCode         = Account Code:\nLabel.AccountNumber       = Account Number:\nLabel.AccountOptions      = Account Options:\nLabel.AccountSeparator    = Account Separator:\nLabel.AccountStatus       = Account Status:\nLabel.AccountType         = Account Type:\nLabel.Action              = Action:\nLabel.Amount              = Amount:\nLabel.AnIntRate           = Annual Interest Rate (APR):\nLabel.Available           = Available:\nLabel.Balance             = Balance:\nLabel.BankAccount         = Bank Account:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Base Account:\nLabel.BaseColor           = Base Colour:\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = Cash Balance:\nLabel.Close               = Close:\nLabel.Color=Colour:\nLabel.Commodity           = Commodity:\nLabel.CompDaysPerYear     = Compounding Days per Year:\nLabel.CompPerTerm         = Compounding Periods per Year:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Count:\nLabel.CreateCurr          = Create Custom Currency:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Currency / Commodity:\nLabel.Currencies          = Currencies:\nLabel.Currency            = Currency:\nLabel.Current             = Current:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Date:\nLabel.DateFormat          = Date Format:\nLabel.DaysPastDue         = Days past due:\nLabel.DefaultCurrency     = Default Currency:\nLabel.DelaySec            = Delay (seconds)\nLabel.Description         = Description:\nLabel.DestAccount         = Destination Account:\nLabel.Difference          = Difference:\nLabel.Dividend            = Dividend:\nLabel.EndDate             = End Date:\nLabel.EndOn               = End On:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Ending Balance:\nLabel.EquityAccount       = Equity Account:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Every:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Exchange Rate:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Fees:\nLabel.FeesAccount         = Fees Account:\nLabel.FileName            = File Name:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Date of first payment:\nLabel.FocusColor          = Focus Colour:\nLabel.Format              = Format:\nLabel.Frequency           = Frequency:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gains:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Height:\nLabel.High                = High:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = CUSIP / ISIN:\nLabel.IncomeAccount       = Income Account:\nLabel.InterestAccount     = Interest Account:\nLabel.LastOccurrence      = Last Occurrence:\nLabel.Layout              = Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = Length of Loan (Months):\nLabel.Low                 = Low:\nLabel.MarketValue         = Market Value:\nLabel.MaxBackupCount      = Maximum number of backup files to keep:\nLabel.Memo                = Memo:\nLabel.Monospace           = Monospace:\nLabel.Name                = Name:\nLabel.NetIncome           = Net Income:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Next payment date:\nLabel.Notes               = Notes:\nLabel.NumTrans            = Number of Transactions:\nLabel.Number              = Number:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date:\nLabel.OpeningBalance      = Opening Balance:\nLabel.OrigLoanAmt         = Original Loan Amount:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Password:\nLabel.Path                = Path\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Payments per Year:\nLabel.Payee               = Payee:\nLabel.Period              = Period:\nLabel.Port                = Port:\nLabel.Prefix              = Prefix:\nLabel.Price               = Price:\nLabel.Proportional        = Proportional:\nLabel.Quantity            = Quantity:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Reconciled Balance:\nLabel.RemindLater         = Remind me again after:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Repeat on:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = Resolution:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Scale:\nLabel.Securities          = Securities:\nLabel.Security            = Security:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Show Empty Accounts\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Start Date:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Start Position:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Statement Date:\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = Suffix:\nLabel.Symbol              = Symbol:\nLabel.TargetBalance       = Target Balance:\nLabel.Top                 = Top:\nLabel.Total               = Total:\nLabel.Transaction         = Transaction:\nLabel.TransferFrom        = Transfer From:\nLabel.TransferTo          = Transfer To:\nLabel.Type                = Type:\nLabel.Units               = Units:\nLabel.UserName            = User Name:\nLabel.Value               = Value:\nLabel.Verify              = Verify:\nLabel.VerifyPassword      = Verify Password:\nLabel.Visible             = Visible:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = _About\\u2026\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = Account\nMenu.AccountRegister.Name             = Account Register\\u2026\nMenu.AddRemoveCurrency.Name=_Add / Remove\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Update Currencies\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = Update Securities\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet\\u2026\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = _Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Cleared\nMenu.Close.Name                       = Close\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters\\u2026\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = C_opy\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = C_urrencies\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Change _default\\u2026\nMenu.DefaultCurrency.Tooltip          = Change the default currency\nMenu.Delete.Name                      = Delete\nMenu.Duplicate.Name                   = Duplicate\nMenu.Edit.Name                        = _Edit\nMenu.EditTranNumList.Name             = Edit Transaction Number List\\u2026\nMenu.Exit.Name                        = _Quit\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = _Export\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archive\\u2026\nMenu.File.Name                        = _File\nMenu.Filter.Name                      = _Filters\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Help\nMenu.Hide.Name                        = Hide\nMenu.HistoryChart.Name                = Historical Chart\\u2026\nMenu.HistoryCommodity.Name            = _History\\u2026\nMenu.HistoryImport.Name               = Historical Import\\u2026\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Income / Expense Pie Chart\\u2026\nMenu.Import.Name                      = _Import\nMenu.ImportAccounts.Name              = Import Accounts\\u2026\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Import jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = List of Accounts\\u2026\nMenu.Locale.Name                      = Change Locale\\u2026\nMenu.LookAndFeel.Name                 = Look and Feel\nMenu.MarkAs.Name                      = Mark As\nMenu.Modify.Name                      = Modify\nMenu.ModifyCommodity.Name             = _Create / Modify\\u2026\nMenu.ModifyCurrency.Name              = _Modify\\u2026\nMenu.ModifyExchangeRates.Name         = _Edit Exchange Rates\\u2026\nMenu.MonthBalance.Name                = Monthly Balance\\u2026\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = End-Of-Month Balance\\u2026\nMenu.MonthEndBalanceCSV.Name          = End-Of-Month Balance (CSV)\\u2026\nMenu.NetWorth.Name                    = Net Worth\\u2026\nMenu.New.Name                         = _New\\u2026\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Open\\u2026\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = _Options\\u2026\nMenu.Option.Tooltip                   = Change program options\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = Periodic Account Balance\\u2026\nMenu.Portfolio.Name                   = Portfolio\\u2026\nMenu.ProfitLoss.Name                  = Profit and Loss\\u2026\nMenu.ProfitLossTXT.Name               = Profit and Loss (Text)\\u2026\nMenu.Reconcile.Name                   = Reconcile\nMenu.Reconciled.Name                  = Reconciled\nMenu.RecurringList.Name               = Recurring Transactions\\u2026\nMenu.Register.Name                    = _Register\\u2026\nMenu.Reports.Name                     = _Reports\nMenu.RunJavaScript.Name               = Run JavaScript\\u2026\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = _Save\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = Save _As\\u2026\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = _Securities\nMenu.SetPassword.Name                 = Set Password\\u2026\nMenu.Show.Name                        = Show\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Themes\nMenu.Tools.Name                       = _Tools\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Unreconciled\nMenu.View.Name                        = _View\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = I have read, understand, and accept the terms of the licenses.\nMessage.AccountAdd                   = Account added\nMessage.AccountCode                  = Account code was not unique: The code was not changed\nMessage.AccountLocked                = Account is Locked\nMessage.AccountModify                = Account modified\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = Account removed\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = AutoSave has been turned off\nMessage.AutoSaveOn                   = AutoSave has been turned on\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Checking for new recurring events\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\\u2026\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Delete the selected transactions?\nMessage.ConfirmReminderDelete        = Delete the selected reminder?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Delete the selected transaction?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Default currency changed to:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine started\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Unable to create account\nMessage.Error.AccountRemove          = Could not remove account\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Could not add commodity\nMessage.Error.AddCurrency            = Could not add currency\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Create a basic account set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = File was not found\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = The license was not accepted\nMessage.Error.LoadingFile            = An error occurred when loading the file\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Could not modify commodity\nMessage.Error.ModifyCurrency         = Could not modify currency\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = File closed\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Default locale changed to:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = Preparing for shutdown\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Report modules were loaded successfully\nMessage.ReportWait                   = Please wait, Loading report modules\nMessage.RestartLocale                = Restart for locale change to take effect\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = Enter starting and ending dates\nMessage.StoreBackup                  = Saving backup to:\nMessage.StoreComplete                = File store is complete\nMessage.StoreWait                    = Waiting for file save to complete\nMessage.TXTFile                      = Text Files (*.txt)\nMessage.TransactionAccountLocked     = Transaction add failed, Destination account(s) are locked\nMessage.TransactionAdd               = Transaction added\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transaction removed\nMessage.TransactionRemoveLocked      = Transaction removal failed, Destination account(s) are locked\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = Commodity is in use\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Currency is in use\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\\n\\nDo you want to continue?\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bank Accounts\nName.ExpenseAccounts = Expense Accounts\nName.IncomeAccounts  = Income Accounts\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = From {0,date,long} To {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Page {0} of {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minutes\nPeriod.15Min     = 15 Minutes\nPeriod.1Day      = 1 Day\nPeriod.1Hr       = 1 Hour\nPeriod.2Hr       = 2 Hours\nPeriod.30Min     = 30 Minutes\nPeriod.5Min      = 5 Minutes\nPeriod.8Hr       = 8 Hours\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Daily\nPeriod.Monthly   = Monthly\nPeriod.NextStart = Next time jGnash starts\nPeriod.None      = None\nPeriod.OnlyOnce  = Only once\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Weekly\nPeriod.Yearly    = Yearly\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = About\nTab.Accounts        = Accounts\nTab.Adjust          = Adjust\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Charge\nTab.Credit          = Credit\nTab.Credits         = Credits\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = Day\nTab.Debit           = Debit\nTab.Formats         = Formats\nTab.GPLLicense      = GPL License\nTab.General         = General\nTab.LGPLLicense     = LGPL License\nTab.License         = License\nTab.Month           = Month\nTab.Network         = Network\nTab.None            = None\nTab.Payment         = Payment\nTab.Register        = Register\nTab.Reminders       = Reminders\nTab.Report          = Report\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = System Information\nTab.Transfer        = Transfer\nTab.Week            = Week\nTab.Year            = Year\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = About\nTitle.AccountBalance             = Account Balance\nTitle.AccountFilter              = Account Filters\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Account Information\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Account Securities\nTitle.AddRemCurr                 = Add / Remove Currencies\nTitle.AmortizationSetup          = Amortization Setup\nTitle.Archive                    = Archive\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Automatic Save\nTitle.Available                  = Available\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Backing Store\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colours\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Cheque Designer\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Column Visibility\nTitle.Colors                     = Colours\nTitle.CommoditiesSecurities      = Securities\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirm\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Create / Modify Securities\nTitle.Credits                    = Credits\nTitle.Currencies                 = Currencies\nTitle.Current                    = Current\nTitle.CutOffDate                 = Select Cut Off Date\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Debits\nTitle.DefDefCurr                 = Define Default Currency\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Display\nTitle.DuplicateTransaction       = Duplicate Transaction\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = End-Of-Month Account Balance\nTitle.EnterPassword              = Enter Password\nTitle.Entry                      = Entry\nTitle.Error                      = Error\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Exchange Rate\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filters\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Frequency\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historical Import\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Import a partial QIF file\nTitle.ImpSum                     = Import Summary\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = Information\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Modify QIF Transactions\nTitle.ModifyAccount              = Modify Account\nTitle.ModifyCurrencies           = Modify Currencies\nTitle.ModifyReminder             = Modify Reminder\nTitle.ModifySecHistory           = Modify Securities History\nTitle.ModifyTransaction          = Modify Transaction\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = New Account\nTitle.NewBudget                  = New Budget\nTitle.NewFile                    = Create a New File\nTitle.NewPassword                = New Password\nTitle.NewReminder                = New Reminder\nTitle.NewTrans                   = New Transaction\nTitle.Notes                      = Notes\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Open\nTitle.Options                    = Options\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Page Setup\nTitle.ParentAccount              = Parent Account\nTitle.PercentDist                = Percent Distribution\nTitle.PercentExpense             = Percent Expense\nTitle.PercentIncome              = Percent Income\nTitle.PleaseWait                 = Please Wait\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profit and Loss Statement\nTitle.ReconcileSettings          = Reconcile Settings\nTitle.Reminder                   = Reminder\nTitle.Reminders                  = Reminders\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Save As\nTitle.SaveFile                   = Save File\nTitle.SecurityHistory            = Security History\nTitle.SelAccount                 = Select Account\nTitle.SelAvailCurr               = Select Available Currencies\nTitle.SelDate                    = Select a Date\nTitle.SelDefCurr                 = Select Default Currency\nTitle.SelDefLocale               = Select Default Locale\nTitle.SelDestAccount             = Select Destination Account\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Select Equity Account\nTitle.SelFile                    = Select File\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = Select Color\nTitle.Selected                   = Selected\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaction\nTitle.Startup                    = Startup\nTitle.Steps                      = Steps\nTitle.Success                    = Success\nTitle.Summary                    = Summary\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaction\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Transaction List\nTitle.TransactionSetup           = Transaction Setup\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visible\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Account list\nToolTip.AccountRegister                      = Account register\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Change column visibility\nToolTip.ConvertSEntry                        = Change this transaction into a Double Entry transaction\nToolTip.DeleteAccount                        = Delete Account\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filter accounts\nToolTip.FilterAccounts                       = Filter by account type\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = Only integers allowed\nToolTip.ModifyAccount                        = Modify account\nToolTip.NewAccount                           = New Account\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = Print register report\nToolTip.ReconcileAccount                     = Reconcile Account\nToolTip.Reminders                            = Reminders\nToolTip.ResizeColumns                        = Resize Columns\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Number of digits to the right of the decimal\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Create a backup with timestamp when closed\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Open a new account register\n\nTransaction.AddShare        = Add Shares (Adjust)\nTransaction.BuyShare        = Buy Shares\nTransaction.Dividend        = Dividend\nTransaction.DoubleEntry     = Double Entry\nTransaction.MergeShare      = Stock Merge\nTransaction.ReinvestDiv     = Reinvest Dividend\nTransaction.RemoveShare     = Remove Shares (Adjust)\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Sell Shares\nTransaction.SingleEntry     = Single Entry\nTransaction.Split           = Split Transaction\nTransaction.SplitEntry      = Split Transaction Entry\nTransaction.SplitShare      = Stock Split\nTransaction.TransferIn      = Transfer Cash In\nTransaction.TransferOut     = Transfer Cash Out\n\nWord.Add             = Add\nWord.All             = All\nWord.Balance         = Balance\nWord.Buy             = Buy\nWord.Commodity       = Commodity\nWord.Copy            = Copy\nWord.Description     = Description\nWord.Difference      = Difference\nWord.Dividend        = Dividend\nWord.Exchange        = Exchange\nWord.Fees            = Fees\nWord.GrossExpense    = Gross Expense\nWord.GrossIncome     = Gross Income\nWord.Interest        = Interest\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = Merge\nWord.Monthly         = By month\nWord.Name            = Name\nWord.NetIncome       = Net Income\nWord.NetWorth        = Net Worth\nWord.NewBudget       = New Budget\nWord.None            = None\nWord.Quarterly       = Quarterly\nWord.ReInvDiv        = Reinvest Dividend\nWord.Remove          = Remove\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Security\nWord.Sell            = Sell\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totals\nWord.Yearly          = Yearly\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_es.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Activo\nAccountType.Bank             = Banco\nAccountType.Cash             = Efectivo\nAccountType.Checking         = Cheques\nAccountType.Credit           = Cr\\u00E9dito\nAccountType.Equity           = Patrimonio\nAccountType.Expense          = Gasto\nAccountType.Income           = Ingreso\nAccountType.Investment       = Inversi\\u00F3n\nAccountType.Liability        = Pasivo\nAccountType.MoneyMarket      = Mercado de dinero\nAccountType.Mutual           = Fondo mutuo\nAccountType.Root             = Ra\\u00EDz\nAccountType.SimpleInvestment = Inversi\\u00F3n sencilla\n\nButton.AccTerms                = Usar t\\u00E9rminos contables\nButton.Accounts                = Cuentas\nButton.AckSel                  = Reconocer seleccionado\nButton.Add                     = A\\u00F1adir\nButton.AllDates                = Todas las fechas\nButton.Amortize                = Amortizar\nButton.AnimationsEnabled       = Habilitar efectos de animaci\\u00F3n\nButton.AnyStatus               = Cualquier estado\nButton.Apply                   = Aplicar\nButton.AssetAccounts           = Cuentas de Activos\nButton.AutoReconcile           = Auto-reconciliar transacciones divididas\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Valores Disponibles\nButton.Back                    = Volver\nButton.BankAccounts            = Cuentas de Bancos\nButton.BudgetMgr               = Gestor de Presupuesto\nButton.Budgeting               = Presupuesto\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Cancelar\nButton.CheckForUpdates         = Buscar nuevas actualizaciones\nButton.CheckReminders          = Revisar recordatorios\nButton.Clear                   = Borrar\nButton.ClearAll                = Borrar Todos\nButton.Cleared                 = Punteado\nButton.Close                   = Cerrar\nButton.Compare                 = Comparar\nButton.ConcatenateMemos        = Combinar notas\nButton.ConfirmReminderDelete   = Confirmar eliminaci\\u00F3n de recordatorios\nButton.ConfirmTransDelete      = Confirmar eliminaci\\u00F3n de transacciones\nButton.CopyToClip              = Copiar al Portapapeles\nButton.CreateTimeFile          = Crear archivo con marca de tiempo al salir\nButton.CreditAccounts          = Cuentas de cr\\u00E9dito\nButton.Delete                  = Eliminar\nButton.DeleteAll               = Eliminar todos\nButton.DeleteWeekends          = Eliminar fines de semana\nButton.DetailSplits            = Mostrar detalle de Divisiones\nButton.Duplicate               = Duplicar\nButton.Edit                    = Editar\nButton.EnableAutoComplete      = Habilitar auto-completar\nButton.Enabled                 = Habilitado\nButton.EndingBalance           = Balance final\nButton.Enter                   = Aceptar\nButton.EnterDaysBefore         = Registrar d\\u00EDas de vencimiento autom\\u00E1ticamente\nButton.ExcludeFromBudget       = Excluir de Presupuestos\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Cuentas de Gastos\nButton.Export                  = Exportar\nButton.ExportSpreadsheet       = Exportar hoja de c\\u00E1lculo\nButton.Filter                  = Filtrar\nButton.Finish                  = Finalizar\nButton.FinishLater             = Finalizar luego\nButton.ForceDefaultCurrency    = Forzar el uso de la moneda predeterminada\nButton.ForceGC                 = Forzar la recolecci\\u00F3n de basura\nButton.HTTPAuth                = Requiere autentificaci\\u00F3n\nButton.Hidden                  = Oculto\nButton.HideAccount             = Cuenta oculta\nButton.HideLockedAccount       = Ocultar cuentas bloqueadas\nButton.HidePlaceholderAccount  = Ocultar cuentas t\\u00EDtulo\nButton.HideZeroBalance         = Ocultar cuentas con saldo cero\nButton.HistoricalFill          = Relleno hist\\u00F3rico\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Incluir Subcuentas\nButton.IncomeAccounts          = Cuentas de Ingresos\nButton.IncomeAndExpense        = Ingresos y Gastos\nButton.Insert                  = Insertar\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Invertir selecci\\u00F3n\nButton.Jump                    = Ir a...\nButton.KeepFridays             = Guarda los viernes\nButton.KeepMondays             = Mantener los lunes\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u00DAltimos 120 d\\u00EDas\nButton.Last12Months            = \\u00DAltimos 12 meses\nButton.Last30Days              = \\u00DAltimos 30 d\\u00EDas\nButton.Last60Days              = \\u00DAltimos 60 d\\u00EDas\nButton.Last90Days              = \\u00DAltimos 90 d\\u00EDas\nButton.LiabilityAccounts       = Cuentas del Pasivo\nButton.Locked                  = Bloqueado\nButton.MatchAccountOnly        = Coincidencia utilizando solo transacciones espec\\u00EDficas de cuenta\nButton.MatchAllTrans           = Emparejar usando todas las transacciones\nButton.MatchCaseSensitive      = Coincidencia es sensible a may\\u00FAsculas\nButton.Modify                  = Modificar\nButton.MonthlyBalance          = Saldo Mensual\nButton.New                     = Nuevo\nButton.NewEmpty                = Nuevo Vac\\u00EDo\nButton.NewHist                 = Nuevo Hist\\u00F3rico\nButton.NewPayment              = Nuevo pago\nButton.Next                    = Siguiente\nButton.No                      = No\nButton.NoEndDate               = Sin fecha final\nButton.None                    = Ning\\u00FAno\nButton.NotReconciled           = No Reconciliado\nButton.Ok                      = Aceptar\nButton.Open                    = Abrir\nButton.OpenLastOnStartup       = Abrir el \\u00FAltimo archivo en el inicio\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Configurar p\\u00E1gina\nButton.PlaceHolder             = Titular\nButton.Portrait                = Portrait\nButton.Print                   = Imprimir\nButton.PrintSample             = Imprimir ejemplo\nButton.Properties              = Propiedades\nButton.Reconcile               = Reconciliar\nButton.ReconcileBoth           = Todas las cuentas de una transacci\\u00F3n tienen el mismo estado reconciliado\nButton.ReconcileDisable        = Deshabilitar reconciliaci\\u00F3n autom\\u00E1tica\nButton.ReconcileIncomeExpense  = Reconciliar autom\\u00E1ticamente las cuentas de Ingresos y Gastos\nButton.Reconciled              = Reconciliado\nButton.Refresh                 = Actualizar\nButton.RegDate                 = Recordar la fecha de la \\u00FAltima transacci\\u00F3n\nButton.Register                = Registro\nButton.RegisterFollowsList     = El registro sigue la lista de cuentas\nButton.RememberPassword        = Recordar Contrase\\u00F1a\nButton.RemindLater             = Recodarmelo m\\u00E1s tarde\nButton.Reminders               = Recordatorios\nButton.RemoteServer            = Servidor remoto\nButton.Remove                  = Quitar\nButton.RemoveOldBackups        = Borrar copias de seguridad antiguas\nButton.Rename                  = Renombrar\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Cambiar tama\\u00F1o\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Redondear a cantidades enteras\nButton.RunningBalance          = Saldo Corriente\nButton.Save                    = Guardar\nButton.SaveFilters             = Guardar Filtros\nButton.SaveImage               = Save Image\nButton.Select                  = Seleccionar\nButton.SelectAll               = Seleccionar todo\nButton.SelectText              = Selecccionar texto en foco\nButton.ShowCommodities         = Mostrar todos los bienes\nButton.ShowEmptyAccounts       = Mostrar cuentas con balance cero\nButton.ShowPercentValues       = Mostrar Porcentajes\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Divisiones\nButton.Start                   = Start\nButton.Stop                    = Detener\nButton.SubstanceAnimations     = Habilitar Animaciones de aspecto Substance\nButton.SumColVis               = Mostrar Columnas Resumen\nButton.SumRowVis               = Mostrar Filas Resumen\nButton.ThemesEnabled           = Habilitar temas\nButton.Timestamp               = Fecha/Hora\nButton.Today                   = Hoy\nButton.UpdateCurrenciesStartup = Actualice las tasas de cambio en el inicio\nButton.UpdateOnline            = Actualizar en l\\u00EDnea\nButton.UpdateSecuritiesStartup = Actualizar valores al inicio\nButton.UseDailyRate            = Usar \\u00EDndice peri\\u00F3dico diario\nButton.UseEncryption           = Encriptar\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Usar nombres largos\nButton.UseProxy                = Usar proxy\nButton.UseRegexForFilter       = Utilizar expresiones regulares para los filtros\nButton.Vertical                = Vertical\nButton.Yes                     = S\\u00ED\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Cuenta\nColumn.AccountName                    = Nombre de cuenta\nColumn.Action                         = Acci\\u00F3n\nColumn.Actual                         = Actual\nColumn.Amount                         = Cantidad\nColumn.Approve                        = Aprobar\nColumn.Balance                        = Balance\nColumn.Budgeted                       = Presupuestado\nColumn.Charge                         = Cargo\nColumn.Close                          = Cerrado\nColumn.Clr                            = Cmp\nColumn.Code                           = Code\nColumn.Commodity                      = Bien\nColumn.CostBasis                      = CB\nColumn.Credit                         = Cr\\u00E9dito\nColumn.Currency                       = Moneda\nColumn.Date                           = Fecha\nColumn.Day                            = D\\u00EDa\nColumn.Debit                          = D\\u00E9bito\nColumn.Decrease                       = Disminuci\\u00F3n\nColumn.Deposit                        = Dep\\u00F3sito\nColumn.Description                    = Descripci\\u00F3n\nColumn.Due                            = Due\nColumn.Enabled                        = Habilitado\nColumn.Entries                        = Entradas\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Tasa de cambio\nColumn.Expense                        = Gastos\nColumn.Freq                           = Frecuencia\nColumn.Gain                           = Ganancia\nColumn.High                           = Alto\nColumn.Income                         = Ingreso\nColumn.Increase                       = Aumento\nColumn.Investment                     = Inversi\\u00F3n\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = P\\u00E9rdida\nColumn.Low                            = Bajo\nColumn.Memo                           = Notas\nColumn.MktValue                       = Valor\nColumn.Month                          = Mes\nColumn.Num                            = N\\u00FAm\nColumn.Payee                          = Beneficiario\nColumn.Payment                        = Pago\nColumn.Percentile                     = Percentile\nColumn.Period                         = Per\\u00EDodo\nColumn.Price                          = Precio\nColumn.Print                          = Imprimir\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Cantidad\nColumn.Rebate                         = Reembolso\nColumn.Receive                        = Recibido\nColumn.ReconciledBalance              = Balance reconciliado\nColumn.Remaining                      = Restante\nColumn.Script                         = Script\nColumn.Security                       = Seguridad\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Cartera %\nColumn.Short.Quantity                 = Cantidad\nColumn.Short.RealizedGain             = R Ganancia\nColumn.Short.RealizedGainPercentage   = R Ganancia %\nColumn.Short.TotalGain                = T Ganancia\nColumn.Short.TotalGainPercentage      = T Ganancia %\nColumn.Short.UnrealizedGain           = No-R Ganancia\nColumn.Short.UnrealizedGainPercentage = No-R Ganancia %\nColumn.Spend                          = Gasto\nColumn.Timestamp                      = Timestamp\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Tipo\nColumn.Value                          = Value\nColumn.Volume                         = Vol\\u00FAmen\nColumn.Withdrawal                     = Retiro\n\nDataStoreType.Bxds = Archivo Binario\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = Archivo XML\n\nItem.Amount         = Cantidad\nItem.CashDeposit    = Dep\\u00F3sito en efectivo\nItem.CashWithdrawal = Retiro en efectivo\nItem.Date           = Fecha\nItem.EFT            = EFT\nItem.Memo           = Notas\nItem.NextNum        = Siguiente No.\nItem.Payee          = Beneficiario\nItem.Trans          = Transferencia\n\nLabel.AccentColor         = Acentuar el color:\nLabel.Account             = Cuenta:\nLabel.AccountCode         = C\\u00F3digo de cuenta:\nLabel.AccountNumber       = N\\u00FAmero de cuenta:\nLabel.AccountOptions      = Opciones de cuenta:\nLabel.AccountSeparator    = Separador de cuentas:\nLabel.AccountStatus       = Estado de Cuenta:\nLabel.AccountType         = Tipo de Cuenta:\nLabel.Action              = Acci\\u00F3n:\nLabel.Amount              = Cantidad:\nLabel.AnIntRate           = Tasa  de inter\\u00E9s anual (APR):\nLabel.Available           = Disponible:\nLabel.Balance             = Balance:\nLabel.BankAccount         = Cuenta Bancaria:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Cuenta Padre:\nLabel.BaseColor           = Color de la base:\nLabel.Bottom              = Bottom:\nLabel.By                  = Por:\nLabel.CashBalance         = Balance de efectivo:\nLabel.Close               = Cerrar:\nLabel.Color=Color:\nLabel.Commodity           = Art\\u00EDculo:\nLabel.CompDaysPerYear     = D\\u00EDas compuestos por a\\u00F1o:\nLabel.CompPerTerm         = Periodos compuestos por a\\u00F1o:\nLabel.Compare             = Comparar:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = N\\u00FAmero:\nLabel.CreateCurr          = Crear moneda personalizada:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Moneda/Bien:\nLabel.Currencies          = Monedas:\nLabel.Currency            = Moneda:\nLabel.Current             = Actual:\nLabel.DatabaseBackend     = Backend Base de Datos:\nLabel.DatabaseName        = Nombre de la Base de Datos:\nLabel.DatabaseServer      = Servidor de Base de Datos:\nLabel.Date                = Fecha:\nLabel.DateFormat          = Formato de fecha:\nLabel.DaysPastDue         = L\\u00EDmite de d\\u00EDas pasados:\nLabel.DefaultCurrency     = Moneda predeterminada:\nLabel.DelaySec            = Retardo (en segundos)\nLabel.Description         = Descripci\\u00F3n:\nLabel.DestAccount         = Cuenta destino:\nLabel.Difference          = Diferencia:\nLabel.Dividend            = Dividendo:\nLabel.EndDate             = Fecha Final:\nLabel.EndOn               = Termina en:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Balance final:\nLabel.EquityAccount       = Cuenta de patrimonio:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Cada:\nLabel.ExchangeAmount      = Monto Cambiado:\nLabel.ExchangeRate        = \\u00CDndice de cambio:\nLabel.ExchangedAmount     = Monto Cambiado:\nLabel.Fees                = Tasa:\nLabel.FeesAccount         = Cuenta de tasas:\nLabel.FileName            = Archivo:\nLabel.FillAll             = Llenar todo:\nLabel.FirstPayDate        = D\\u00EDa del primer pago:\nLabel.FocusColor          = Enfoque Color:\nLabel.Format              = Format:\nLabel.Frequency           = Frecuencia:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Ganancias:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Altura:\nLabel.High                = Alto:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Cuenta de Ingresos:\nLabel.InterestAccount     = Cuenta de Inter\\u00E9ses:\nLabel.LastOccurrence      = \\u00DAltima ocurrencia:\nLabel.Layout              = Disposici\\u00F3n\nLabel.Left                = Left:\nLabel.LoanTerm            = Plazo del pr\\u00E9stamo (meses):\nLabel.Low                 = Bajo:\nLabel.MarketValue         = Valor de mercado:\nLabel.MaxBackupCount      = N\\u00FAmero m\\u00E1ximo de archivos de copia de seguridad a mantener:\nLabel.Memo                = Notas:\nLabel.Monospace           = Monoespaciada:\nLabel.Name                = Nombre:\nLabel.NetIncome           = Ingreso neto:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Pr\\u00F3ximo d\\u00EDa de pago:\nLabel.Notes               = Notas:\nLabel.NumTrans            = N\\u00FAmero de transacciones:\nLabel.Number              = N\\u00FAmero:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Fecha de Apertura:\nLabel.OpeningBalance      = Balance apertura:\nLabel.OrigLoanAmt         = Cantidad original del pr\\u00E9stamo:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Contrase\\u00F1a:\nLabel.Path                = Ruta:\nLabel.Pattern             = Patr\\u00F3n:\nLabel.PayPerTerm          = Pagos por a\\u00F1o:\nLabel.Payee               = Beneficiario:\nLabel.Period              = Per\\u00EDodo:\nLabel.Port                = Puerto:\nLabel.Prefix              = Prefijo:\nLabel.Price               = Precio:\nLabel.Proportional        = Proporcionada:\nLabel.Quantity            = Cantidad:\nLabel.QuoteSource         = Fuente de Cotizaci\\u00F3n:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Balance reconciliado:\nLabel.RemindLater         = Record\\u00E1rmelo nuevamente despu\\u00E9s de:\nLabel.RenameBudget        = Renombrar Presupuesto a:\nLabel.RepeatOn            = Repetir en:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = Resolution:\nLabel.ReturnOfCapital     = Retorno de Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Tasa:\nLabel.Securities          = T\\u00EDtulos:\nLabel.Security            = T\\u00EDtulo:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Mostrar Cuentas Vac\\u00EDas\nLabel.SortOrder           = Criterio de ordenaci\\u00F3n:\nLabel.SpreadsheetFiles    = Archivos de hoja de c\\u00E1lculo\nLabel.StartDate           = Fecha inicial:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Posici\\u00F3n inicial:\nLabel.StartRow            = Fila inicial:\nLabel.StatementDate       = Fecha de Declaraci\\u00F3n:\nLabel.StorageType         = Tipo de almacenamiento:\nLabel.Suffix              = Sufijo:\nLabel.Symbol              = S\\u00EDmbolo:\nLabel.TargetBalance       = Balance buscado:\nLabel.Top                 = Top:\nLabel.Total               = Total:\nLabel.Transaction         = Transacci\\u00F3n:\nLabel.TransferFrom        = Transferido de:\nLabel.TransferTo          = Transferir a:\nLabel.Type                = Tipo:\nLabel.Units               = Units:\nLabel.UserName            = Usuario:\nLabel.Value               = Value:\nLabel.Verify              = Verificar:\nLabel.VerifyPassword      = Verificar Contrase\\u00F1a:\nLabel.Visible             = Visible:\nLabel.Volume              = Vol\\u00FAmen:\nLabel.Width               = Width:\nLabel.XMLFiles            = Archivos XML\nLabel.Year                = A\\u00F1o:\nLabel.jGnashFiles         = Archivos jGnash\n\nMenu.About.Name                       = _Acerca de\\u2026\nMenu.About.Tooltip                    = Informaci\\u00F3n acerca de jGnash\nMenu.Account.Name                     = Cuenta\nMenu.AccountRegister.Name             = Registro de cuentas...\nMenu.AddRemoveCurrency.Name=_A\\u00F1adir / Eliminar\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Actualizar Divisas\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = Actualizar t\\u00EDtulos\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance General...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = _Gestor de Presupuesto\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Punteado\nMenu.Close.Name                       = _Cerrar\nMenu.Close.Tooltip                    = Cerrar el Archivo Activo\nMenu.CloseAllWindows.Name             = Cerrar todas las ventanas\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters\\u2026\nMenu.Console.Name                     = Ventana Consola\\u2026\nMenu.Copy.Name                        = _Copiar\nMenu.Copy.Tooltip                     = Crear un duplicado del item seleccionado\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _Monedas\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Establecer _predeterminada...\nMenu.DefaultCurrency.Tooltip          = Cambiar la moneda predeterminada\nMenu.Delete.Name                      = Eliminar\nMenu.Duplicate.Name                   = Duplicar\nMenu.Edit.Name                        = _Editar\nMenu.EditTranNumList.Name             = Editar lista de n\\u00FAmeros de transacci\\u00F3n\nMenu.Exit.Name                        = _Salir\nMenu.Exit.Tooltip                     = Salir de jGnash\nMenu.Export.Name                      = _Exportar\nMenu.ExportAccounts.Name              = Exportar Cuentas...\nMenu.File.Archive                     = Archivar...\nMenu.File.Name                        = A_rchivo\nMenu.Filter.Name                      = _Filtros\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = A_yuda\nMenu.Hide.Name                        = Ocultar\nMenu.HistoryChart.Name                = Gr\\u00E1fico hist\\u00F3rico...\nMenu.HistoryCommodity.Name            = _Hist\\u00F3rico...\nMenu.HistoryImport.Name               = Historico de importaci\\u00F3n ...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Gr\\u00E1fico de pastel de Ingresos / Gastos\nMenu.Import.Name                      = _Importar\nMenu.ImportAccounts.Name              = Importar Cuentas...\nMenu.ImportAccounts.Tooltip           = Importar archivo de Cuentas jGnash\nMenu.ImportJgnash.Name                = Importar jGnash...\nMenu.ImportJgnash.Tooltip             = Importar Archivos jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Importar archivo SWIFT MT940\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Importar archivos OFX y QFX\nMenu.ImportQif.Name                   = _QIF...\nMenu.Jump.Name                        = Saltar\nMenu.ListOfAccounts.Name              = _Lista de Cuentas\\u2026\nMenu.Locale.Name                      = Cambiar Idioma...\nMenu.LookAndFeel.Name                 = Apariencia del programa\nMenu.MarkAs.Name                      = Marcar\nMenu.Modify.Name                      = Modificar\nMenu.ModifyCommodity.Name             = _Crear/Modificar...\nMenu.ModifyCurrency.Name              = _Modificar...\nMenu.ModifyExchangeRates.Name         = Editar Tasas de Cambio ...\nMenu.MonthBalance.Name                = Balance mensual ...\nMenu.MonthBalanceCompare.Name         = Comparaci\\u00F3n Balance Mensual ...\nMenu.MonthEndBalance.Name             = Balance de fin de mes...\nMenu.MonthEndBalanceCSV.Name          = Balance de fin de mes (CSV)...\nMenu.NetWorth.Name                    = Patrimonio Neto...\nMenu.New.Name                         = _Nuevo\\u2026\nMenu.New.Tooltip                      = Crear archivo nuevo\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Abrir\\u2026\nMenu.Open.Tooltip                     = Abrir un archivo espec\\u00EDfico\nMenu.Option.Name                      = _Opciones...\nMenu.Option.Tooltip                   = Cambiar configuraciones\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Gr\\u00E1fico circular de Ingresos / Gastos por beneficiario\\u2026\nMenu.PeriodicAccountBalance.Name      = Saldo de la cuenta peri\\u00F3dica\\u2026\nMenu.Portfolio.Name                   = Portfolio...\nMenu.ProfitLoss.Name                  = P\\u00E9rdidas y ganacias...\nMenu.ProfitLossTXT.Name               = P\\u00E9rdidas y ganacias (texto) ...\nMenu.Reconcile.Name                   = Reconciliar\nMenu.Reconciled.Name                  = Reconciliado\nMenu.RecurringList.Name               = Transacciones repetitivas\nMenu.Register.Name                    = _Registro...\nMenu.Reports.Name                     = _Reportes\nMenu.RunJavaScript.Name               = Ejecutar JavaScript...\nMenu.RunJavaScript.Tooltip            = Ejecutar JavaScript\nMenu.Save.Name                        = _Guardar\nMenu.Save.Tooltip                     = Guardar el archivo actual\nMenu.SaveAs.Name                      = Guardar c_omo...\nMenu.SaveAs.Tooltip                   = Guardar el archivo actual con un nuevo nombre\nMenu.Securities.Name                  = _Bienes\nMenu.SetPassword.Name                 = Establecer contrase\\u00F1a...\nMenu.Show.Name                        = Mostrar\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Temas\nMenu.Tools.Name                       = _Herramientas\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = No-Reconciliado\nMenu.View.Name                        = _Ver\nMenu.Window.Name                      = Ventana\n\nMessage.AcceptLicense                = He le\\u00EDdo, entendido y aceptado los t\\u00E9rminos de las licencias\nMessage.AccountAdd                   = Cuenta a\\u00F1adida\nMessage.AccountCode                  = El c\\u00F3digo de cuenta ya existe, no se realiz\\u00F3 el cambio\nMessage.AccountLocked                = La cuenta esta bloqueada\nMessage.AccountModify                = Cuenta Modificada\nMessage.AccountMoveFailed            = No se puede mover la cuenta\nMessage.AccountRemove                = Cuenta eliminada\nMessage.AntiAlias                    = Habilitar antialiasing del texto!\nMessage.AutoSaveOff                  = Auto-guardar est\\u00E1 deshabilitado\nMessage.AutoSaveOn                   = Auto-guardar est\\u00E1 habilitado\nMessage.CSVFile                      = Archivo separado por comas (*.csv)\nMessage.CheckRecurring               = Buscando transacciones recurrentes\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Recolectando datos del informe\nMessage.CompilingReport              = Compilando informe\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = \\u00BFDesea eliminar las transacciones seleccionadas?\nMessage.ConfirmReminderDelete        = \\u00BFDesea eliminar los recordatorios seleccionados?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = \\u00BFDesea eliminar la transacci\\u00F3n seleccionada?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = La moneda predeterminada fue cambiada a:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Motor iniciado\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = No se puede crear la cuenta\nMessage.Error.AccountRemove          = No se puede eliminar la cuenta\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = No se pudo a\\u00F1adir  el bien\nMessage.Error.AddCurrency            = No se puede agregar la moneda\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Cree un conjunto b\\u00E1sico de cuentas primeramente\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = No se encontr\\u00F3 el archivo\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Grupo de cuenta inv\\u00E1lido\nMessage.Error.InvalidTransactionTag  = Etiqueta de Transacci\\u00F3n Inv\\u00E1lida\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = No acept\\u00F3 la licencia\nMessage.Error.LoadingFile            = Error al cargar el archivo\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = No se pudo modificar el bien\nMessage.Error.ModifyCurrency         = No se pudo modificar la cuenta\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = No se proces\\u00F3 ninguna transacci\\u00F3n\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Archivo cerrado\nMessage.FileIsLocked                 = El archivo esta bloqueado por otra aplicaci\\u00F3n\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = El archivo seleccionado no es valido !!!\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Espere, importar datos puede tardar un poco\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requiere JVM 11 o superior\nMessage.LoadReportFail               = No se pudo cargar la definici\\u00F3n del reporte\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Localizaci\\u00F3n predeterminada cambiada a:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = No repetir\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = La base de datos existente ser\\u00E1 reemplazada !!!\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Fall\\u00F3 la interpretaci\\u00F3n de la definici\\u00F3n del reporte\nMessage.PleaseWait                   = Espere por favor...\nMessage.PrefFail                     = No se han encontrado las preferencias anteriores\nMessage.PrepShutdown                 = Prepar\\u00E1ndose para finalizar\nMessage.ProcessingReportData         = Procesando datos del informe\nMessage.Proxy                        = Configurando Proxy http:\nMessage.ReduceFont                   = Pruebe a reducir el tama\\u00F1o de fuente.\\nPor favor, consulte la ayuda de Reportes para obtener m\\u00E1s informaci\\u00F3n.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Los m\\u00F3dulos de informes se cargaron exitosamente\nMessage.ReportWait                   = Espere, se est\\u00E1n cargando los m\\u00F3dulos de informes\nMessage.RestartLocale                = Cierre y vuelva a abrir el programa para ver la nueva localizaci\\u00F3n\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Buscando, Espere\nMessage.Shutdown                     = Cierre\nMessage.StartEndDate                 = Ingrese la fecha inicial y final\nMessage.StoreBackup                  = Guardando copia de seguridad en:\nMessage.StoreComplete                = Se complet\\u00F3 el almacenamiento del archivo\nMessage.StoreWait                    = Esperando a que se complete el almacenamiento del archivo\nMessage.TXTFile                      = Archivos de texto (*.txt)\nMessage.TransactionAccountLocked     = Fallo al agregar la transacci\\u00F3n. La cuenta (o cuentas) de destino est\\u00E1 bloqueada\nMessage.TransactionAdd               = Transacci\\u00F3n agregada\nMessage.TransactionModifyLocked      = La transacci\\u00F3n no se puede modificar.\\nLa cuenta de destino est\\u00E1 bloqueada contra modificaciones.\nMessage.TransactionRemove            = Transacci\\u00F3n eliminada\nMessage.TransactionRemoveLocked      = Fallo al eliminar la transacci\\u00F3n. La cuenta (o cuentas) de destino est\\u00E1 bloqueada\nMessage.UninstallBad                 = No se ha podido desinstalar con \\u00E9xito\nMessage.UninstallGood                = Desinstalado con \\u00E9xito!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = Utiliza la versi\\u00F3n\nMessage.Warn.CommodityInUse          = El bien est\\u00E1 siendo usado\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = La moneda est\\u00E1 siendo usada\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Cuentas de Banco\nName.ExpenseAccounts = Cuentas de Gastos\nName.IncomeAccounts  = Cuentas de Ingresos\nName.Root            = Ra\\u00EDz\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Desde  {0,date,long} hasta {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = P\\u00E1gina {0} de {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 minutos\nPeriod.15Min     = 15 minutos\nPeriod.1Day      = 1 d\\u00EDa\nPeriod.1Hr       = 1 hora\nPeriod.2Hr       = 2 horas\nPeriod.30Min     = 30 minutos\nPeriod.5Min      = 5 minutos\nPeriod.8Hr       = 8 horas\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Diario\nPeriod.Monthly   = Mensual\nPeriod.NextStart = La pr\\u00F3xima vez que jGnash inicie\nPeriod.None      = Ninguno\nPeriod.OnlyOnce  = Solo una vez\nPeriod.Quarterly = Trimestral\nPeriod.Weekly    = Semanalmente\nPeriod.Yearly    = Anualmente\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = Ninguno\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! Reino Unido e Irlanda\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = Acerca de\nTab.Accounts        = Accounts\nTab.Adjust          = Ajuste\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Presupuesto\nTab.Charge          = Cargo\nTab.Credit          = Cr\\u00E9dito\nTab.Credits         = Cr\\u00E9ditos\nTab.DataEngine      = Motor de datos\nTab.DataProviders   = Data Providers\nTab.Day             = D\\u00EDa\nTab.Debit           = D\\u00E9bito\nTab.Formats         = Formats\nTab.GPLLicense      = Licencia GPL\nTab.General         = General\nTab.LGPLLicense     = Licencia LGPL\nTab.License         = Licencia\nTab.Month           = Mes\nTab.Network         = Red\nTab.None            = Ninguno\nTab.Payment         = Pago\nTab.Register        = Registro\nTab.Reminders       = Recordatorios\nTab.Report          = Reporte\nTab.StartupShutdown = Inicio / Cierre\nTab.SysInfo         = Informaci\\u00F3n del sistema\nTab.Transfer        = Transferencia\nTab.Week            = Semana\nTab.Year            = A\\u00F1o\n\nTag.Bank                   = Banco\nTag.Dividend               = Dividendo\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Ganancias/(P\\u00E9rdidas)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Tasa de Inversi\\u00F3n\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = Acerca de\nTitle.AccountBalance             = Balance mensual\nTitle.AccountFilter              = Filtros de cuenta\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Informaci\\u00F3n de cuenta\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Cuenta de inversiones\nTitle.AddRemCurr                 = A\\u00F1adir/Eliminar monedas\nTitle.AmortizationSetup          = Configurar amortizaci\\u00F3n\nTitle.Archive                    = Archivo\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Guardar autom\\u00E1ticamente\nTitle.Available                  = Disponible\nTitle.BackgroundUpdate           = Actualizaci\\u00F3nes Autom\\u00E1ticas\nTitle.BackingStore               = Lugar para guardar\nTitle.BalanceSheet               = Balance General\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Dise\\u00F1ador de cheques\nTitle.ChooseAccounts             = Elija las Cuentas a Crear\nTitle.ColVis                     = Visibilidad de columnas\nTitle.Colors                     = Colores\nTitle.CommoditiesSecurities      = Inversiones\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirmar\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Cerrar Ventana\nTitle.CreateModifyCommodities    = Crear/Modificar bienes\nTitle.Credits                    = Cr\\u00E9ditos\nTitle.Currencies                 = Monedas\nTitle.Current                    = Corriente\nTitle.CutOffDate                 = Fecha de corte\nTitle.DatabaseCfg                = Configuraci\\u00F3n de la Base de Datos\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = D\\u00E9bitos\nTitle.DefDefCurr                 = Definir moneda predeterminada\nTitle.DefTranNum                 = N\\u00FAmeros de transacci\\u00F3n predetarminados\nTitle.DefaultBehavior            = Comportamiento Predeterminado\nTitle.Defaults                   = Valores predeterminados\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Vista\nTitle.DuplicateTransaction       = Duplicar transacci\\u00F3n\nTitle.DuplicateTransactionsFound = Se encontraron transacciones duplicadas\nTitle.EditExchangeRates          = Editar Tasas de cambio\nTitle.EndMonthBalance            = Balance de fin de mes\nTitle.EnterPassword              = Ingrese su contrase\\u00F1a\nTitle.Entry                      = Entrada\nTitle.Error                      = Error\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = \\u00CDndice de cambio\nTitle.FileImport                 = Seleccione el archivo a importar\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filtros\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Fuentes\nTitle.Frequency                  = Frecuencia\nTitle.HTTPProxy                  = Proxy HTTP\nTitle.Help                       = Ayuda jGnash\nTitle.HistoryImport              = Importaci\\u00F3n hist\\u00F3rica\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Importar un archivo QIF parcial\nTitle.ImpSum                     = Res\\u00FAmen de la importaci\\u00F3n\nTitle.ImportOFX                  = Importar un archivo OFX\nTitle.ImportTransactions         = Importar transactiones de un archivo\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = informaci\\u00F3n\nTitle.InvFees                    = Tasas de Inversi\\u00F3n\nTitle.InvGainsLoss               = Imverci\\u00F3n Ganancia / P\\u00E9rdida\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modificar transacci\\u00F3nes\nTitle.ModOFXTrans                = Modificar transacci\\u00F3nes OFX\nTitle.ModQIFTrans                = Modificar transacci\\u00F3nes  QIF\nTitle.ModifyAccount              = Modificar cuenta\nTitle.ModifyCurrencies           = Modificar monedas\nTitle.ModifyReminder             = Modificar recordatorio\nTitle.ModifySecHistory           = Modificar hist\\u00F3rico de bienes\nTitle.ModifyTransaction          = Modificar transacci\\u00F3n\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nueva Cuenta\nTitle.NewBudget                  = Nuevo Presupuesto\nTitle.NewFile                    = Nuevo archivo\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nuevo recordatorio\nTitle.NewTrans                   = Nueva transacci\\u00F3n\nTitle.Notes                      = Notas\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Abrir\nTitle.Options                    = Opciones\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Configurar p\\u00E1gina\nTitle.ParentAccount              = Cuenta padre\nTitle.PercentDist                = Distribuci\\u00F3n porcentual\nTitle.PercentExpense             = Procentaje de gasto\nTitle.PercentIncome              = Porcentaje de ingreso\nTitle.PleaseWait                 = Por favor, espere...\nTitle.PortfolioReport            = Informe de cartera\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Balance de p\\u00E9rdidas y ganancias\nTitle.ReconcileSettings          = Configurar reconciliaci\\u00F3n\nTitle.Reminder                   = Recordatorio\nTitle.Reminders                  = Recordatorios\nTitle.RenameBudget               = Renombrar Presupuesto\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Guardar Como...\nTitle.SaveFile                   = Guardar archivo\nTitle.SecurityHistory            = Modificar hist\\u00F3rico de inversiones\nTitle.SelAccount                 = Seleccionar cuenta\nTitle.SelAvailCurr               = Seleccionar monedas disponibles\nTitle.SelDate                    = Seleccionar fecha\nTitle.SelDefCurr                 = Seleccionar moneda predeterminada\nTitle.SelDefLocale               = Seleccionar localizaci\\u00F3n predeterminada\nTitle.SelDestAccount             = Seleccionar cuenta de destino\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Seleccionar cuenta de patrimonio\nTitle.SelFile                    = Seleccionar archivo\nTitle.SelQifDateFormat           = Elegir formato de fecha para la importaci\\u00F3n de QIF\nTitle.SelectColor                = Seleccionar color\nTitle.Selected                   = Seleccionado\nTitle.Shutdown                   = Cierre\nTitle.SmartFill                  = Llenado Inteligente\nTitle.SpitTran                   = Dividir Transacci\\u00F3n\nTitle.Startup                    = Inicio\nTitle.Steps                      = Pasos\nTitle.Success                    = \\u00C9xito\nTitle.Summary                    = Resumen\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transacci\\u00F3n\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Lista de transacciones\nTitle.TransactionSetup           = Configurar transacci\\u00F3n\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Excepci\\u00F3n libre\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visible\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Lista de cuentas\nToolTip.AccountRegister                      = Registro de cuentas\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Cambiar visibilidad de la columna\nToolTip.ConvertSEntry                        = Convertir en transacci\\u00F3n de doble entrada\nToolTip.DeleteAccount                        = Eliminar cuenta\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filtrar cuentas\nToolTip.FilterAccounts                       = Filtrar por tipo de cuenta\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Cambiar tama\\u00F1o de fuente\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Ayuda\nToolTip.ISIN                                 = C\\u00F3digo ISIN\nToolTip.IntegersOnly                         = s\\u00F3lo enteros permitidos\nToolTip.ModifyAccount                        = Modificar cuenta\nToolTip.NewAccount                           = Nueva cuenta\nToolTip.PageSetup                            = Configurar p\\u00E1gina\nToolTip.PrintRegRep                          = Imprimir reporte del registro\nToolTip.ReconcileAccount                     = Reconciliar cuenta\nToolTip.Reminders                            = Recordatorios\nToolTip.ResizeColumns                        = Cambiar el tama\\u00F1o de columnas\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = N\\u00FAmero de d\\u00EDgitos a la derecha del decimal\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Crear copia de seguridad con la fecha y hora al cerrar\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Abrir un nuevo registro de la cuenta\n\nTransaction.AddShare        = A\\u00F1adir acciones\nTransaction.BuyShare        = Comprar acciones\nTransaction.Dividend        = Dividendo\nTransaction.DoubleEntry     = Partida doble\nTransaction.MergeShare      = Combinar stock\nTransaction.ReinvestDiv     = Reinvertir dividendo\nTransaction.RemoveShare     = Eliminar acci\\u00F3n\nTransaction.ReturnOfCapital = Retorno de Capital\nTransaction.SellShare       = Vender acciones\nTransaction.SingleEntry     = Partida \\u00FAnica\nTransaction.Split           = Transacci\\u00F3n dividida\nTransaction.SplitEntry      = Entrada de transacci\\u00F3n dividida\nTransaction.SplitShare      = Dividir acciones\nTransaction.TransferIn      = Transferencia entrante\nTransaction.TransferOut     = Transferencia saliente\n\nWord.Add             = A\\u00F1adir\nWord.All             = Todos\nWord.Balance         = Balance\nWord.Buy             = Comprar\nWord.Commodity       = Bienes\nWord.Copy            = Copy\nWord.Description     = Descripci\\u00F3n\nWord.Difference      = Diferencia\nWord.Dividend        = Dividendo\nWord.Exchange        = Cambio\nWord.Fees            = Tasas\nWord.GrossExpense    = Gasto bruto\nWord.GrossIncome     = Ingreso bruto\nWord.Interest        = Inter\\u00E9s\nWord.Into            = en\nWord.Invalid         = Inv\\u00E1lido\nWord.Merge           = ir\nWord.Monthly         = Mensual\nWord.Name            = Nombre\nWord.NetIncome       = Ingreso Neto\nWord.NetWorth        = Patrimonio Neto\nWord.NewBudget       = Nuevo Presupuesto\nWord.None            = Ninguno\nWord.Quarterly       = Trimestral\nWord.ReInvDiv        = Reinvertir dividendo:\nWord.Remove          = Eliminar\nWord.ReturnOfCapital = Retorno de Capital\nWord.Seconds         = segundos\nWord.Security        = Inversiones\nWord.Sell            = Vender\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totales\nWord.Yearly          = Anual\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_fr.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Actif\nAccountType.Bank             = Banque\nAccountType.Cash             = Esp\\u00E8ce\nAccountType.Checking         = Checking\nAccountType.Credit           = Cr\\u00E9dit\nAccountType.Equity           = Capitaux propres\nAccountType.Expense          = D\\u00E9pense\nAccountType.Income           = Revenu\nAccountType.Investment       = Investissement\nAccountType.Liability        = Dettes\nAccountType.MoneyMarket      = March\\u00E9 mon\\u00E9taire\nAccountType.Mutual           = OPCVM\nAccountType.Root             = Racine\nAccountType.SimpleInvestment = Investissement simple\n\nButton.AccTerms                = Utilisation des termes comptables\nButton.Accounts                = Comptes\nButton.AckSel                  = Acquittement s\\u00E9lectionn\\u00E9\nButton.Add                     = Ajouter\nButton.AllDates                = Toutes les dates\nButton.Amortize                = Amortir\nButton.AnimationsEnabled       = Activer les effets d'animation\nButton.AnyStatus               = Tout statut\nButton.Apply                   = Appliquer\nButton.AssetAccounts           = Comptes d'actifs\nButton.AutoReconcile           = Rapprochement automatique des transactions r\\u00E9parties\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = Pr\\u00E9c\\u00E9dent\nButton.BankAccounts            = Comptes bancaires\nButton.BudgetMgr               = Gestionnaire de budget\nButton.Budgeting               = Budg\\u00E9tisation\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Annuler\nButton.CheckForUpdates         = Recherche de nouvelles mises \\u00E0 jour\nButton.CheckReminders          = V\\u00E9rifiez les rappels\nButton.Clear                   = Effacer\nButton.ClearAll                = Tout effacer\nButton.Cleared                 = Point\\u00E9\nButton.Close                   = Fermer\nButton.Compare                 = Comparer\nButton.ConcatenateMemos        = Combiner des m\\u00E9mos\nButton.ConfirmReminderDelete   = Confirmez la suppression du rappel\nButton.ConfirmTransDelete      = Confirmez la suppression de la transaction\nButton.CopyToClip              = Copier dans le presse-papier\nButton.CreateTimeFile          = Cr\\u00E9er un fichier timestamp\\u00E9 \\u00E0 la sortie\nButton.CreditAccounts          = Comptes de cr\\u00E9dit\nButton.Delete                  = Supprimer\nButton.DeleteAll               = Tout supprimer\nButton.DeleteWeekends          = Supprimer les week-ends\nButton.DetailSplits            = D\\u00E9tailler les r\\u00E9partitions\nButton.Duplicate               = Dupliquer\nButton.Edit                    = Edition\nButton.EnableAutoComplete      = Activer la saisie automatique\nButton.Enabled                 = Activer\nButton.EndingBalance           = Solde de cl\\u00F4ture\nButton.Enter                   = Entrer\nButton.EnterDaysBefore         = Enregistre automatiquement l'op\\u00E9ration avant\nButton.ExcludeFromBudget       = Exclure des budgets\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Comptes de d\\u00E9pense\nButton.Export                  = Exportation\nButton.ExportSpreadsheet       = Export Tableur\nButton.Filter                  = Filtre\nButton.Finish                  = Terminer\nButton.FinishLater             = Terminer plus tard\nButton.ForceDefaultCurrency    = Utilisation forc\\u00E9e de la devise par d\\u00E9faut\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = N\\u00E9cessite une authentification\nButton.Hidden                  = Cach\\u00E9\nButton.HideAccount             = Compte cach\\u00E9\nButton.HideLockedAccount       = Masquer les comptes verrouill\\u00E9s\nButton.HidePlaceholderAccount  = Masquer les comptes d'espace r\\u00E9serv\\u00E9\nButton.HideZeroBalance         = Masquer les comptes de solde z\\u00E9ro\nButton.HistoricalFill          = Remplissage historique\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Sous Inclure les comptes\nButton.IncomeAccounts          = Comptes de revenu\nButton.IncomeAndExpense        = Revenus et d\\u00E9penses\nButton.Insert                  = Insert\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Inverser la s\\u00E9lection\nButton.Jump                    = Jump\nButton.KeepFridays             = Gardez les vendredis\nButton.KeepMondays             = Gardez les lundis\nButton.Landscape               = Landscape\nButton.Last120Days             = Derniers 120 jours\nButton.Last12Months            = Les 12 derniers mois\nButton.Last30Days              = Les 30 derniers jours\nButton.Last60Days              = Derniers 60 jours\nButton.Last90Days              = Derniers 90 jours\nButton.LiabilityAccounts       = Comptes de responsabilit\\u00E9\nButton.Locked                  = Verrouill\\u00E9\nButton.MatchAccountOnly        = Utiliser uniquement des transactions sp\\u00E9cifiques au compte\nButton.MatchAllTrans           = Utiliser toutes les transactions\nButton.MatchCaseSensitive      = Le match est sensible \\u00E0 la casse\nButton.Modify                  = Modifier\nButton.MonthlyBalance          = Solde Mensuel\nButton.New                     = Nouveau\nButton.NewEmpty                = Nouveau vide\nButton.NewHist                 = New Historical\nButton.NewPayment              = Nouveau paiement\nButton.Next                    = Suivant\nButton.No                      = Non\nButton.NoEndDate               = Pas de date de fin\nButton.None                    = Aucun\nButton.NotReconciled           = Non reconcili\\u00E9\nButton.Ok                      = OK\nButton.Open                    = Ouvrir\nButton.OpenLastOnStartup       = Ouvrir le dernier fichier au d\\u00E9marrage\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Mise en page\nButton.PlaceHolder             = Fictif\nButton.Portrait                = Portrait\nButton.Print                   = Impression\nButton.PrintSample             = Impression d'un exemplaire\nButton.Properties              = Propri\\u00E9t\\u00E9s\nButton.Reconcile               = Rapprocher\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disable automatic reconciliation\nButton.ReconcileIncomeExpense  = Automatically reconcile Income and Expense Accounts\nButton.Reconciled              = Rapproch\\u00E9\nButton.Refresh                 = Rafra\\u00EEchir\nButton.RegDate                 = M\\u00E9moriser la date de la derni\\u00E8re transaction\nButton.Register                = Op\\u00E9rations\nButton.RegisterFollowsList     = Liste des op\\u00E9rations synchronis\\u00E9es avec la liste des comptes\nButton.RememberPassword        = Remember Password\nButton.RemindLater             = Me rappeler plus tard\nButton.Reminders               = Rappels\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Supprimer\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Redimensionner\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Solde en cours\nButton.Save                    = Enregistrer\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = S\\u00E9lectionner\nButton.SelectAll               = Tout s\\u00E9lectionner\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Show all Commodities\nButton.ShowEmptyAccounts       = Montrer les comptes de balance nulle\nButton.ShowPercentValues       = Show Percent Values\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = R\\u00E9partitions\nButton.Start                   = D\\u00E9marrer\nButton.Stop                    = Arr\\u00EAt\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Th\\u00E8mes activ\\u00E9s\nButton.Timestamp               = Horodateur\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Update currency exchange rates on startup\nButton.UpdateOnline            = Mise \\u00E0 jour en ligne\nButton.UpdateSecuritiesStartup = Update securities on startup\nButton.UseDailyRate            = Utilise le taux journalier\nButton.UseEncryption           = Utiliser l'encryption\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Utiliser les noms longs\nButton.UseProxy                = Utiliser un proxy\nButton.UseRegexForFilter       = Utilisez des expressions r\\u00E9guli\\u00E8res pour les filtres\nButton.Vertical                = Vertical\nButton.Yes                     = Oui\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Compte\nColumn.AccountName                    = Nom du compte\nColumn.Action                         = Op\\u00E9ration\nColumn.Actual                         = Actual\nColumn.Amount                         = Montant\nColumn.Approve                        = Approuver\nColumn.Balance                        = Solde\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = D\\u00E9bit\nColumn.Close                          = Close\nColumn.Clr                            = V\\u00E9rifi\\u00E9\nColumn.Code                           = Code\nColumn.Commodity                      = Bien\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Cr\\u00E9dit\nColumn.Currency                       = Devise\nColumn.Date                           = Date\nColumn.Day                            = Jour\nColumn.Debit                          = D\\u00E9bit\nColumn.Decrease                       = Diminution\nColumn.Deposit                        = Versement\nColumn.Description                    = Description\nColumn.Due                            = Due\nColumn.Enabled                        = Activ\\u00E9\nColumn.Entries                        = Ecriture\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Exchange Rate\nColumn.Expense                        = D\\u00E9pense\nColumn.Freq                           = Fr\\u00E9quence\nColumn.Gain                           = Gain\nColumn.High                           = Haut\nColumn.Income                         = Revenu\nColumn.Increase                       = Augmentation\nColumn.Investment                     = Titre\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Loss\nColumn.Low                            = Bas\nColumn.Memo                           = Notes\nColumn.MktValue                       = Value\nColumn.Month                          = Mois\nColumn.Num                            = N\\u00B0\nColumn.Payee                          = B\\u00E9n\\u00E9ficiaire\nColumn.Payment                        = Paiement\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Prix\nColumn.Print                          = Imprimer\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Quantit\\u00E9\nColumn.Rebate                         = Remboursement\nColumn.Receive                        = Recevoir\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = D\\u00E9pens\\u00E9\nColumn.Timestamp                      = Timestamp\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Type\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Retrait\n\nDataStoreType.Bxds = Fichier binaire\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Montant\nItem.CashDeposit    = D\\u00E9p\\u00F4t d'esp\\u00E8ce\nItem.CashWithdrawal = Retrait d'esp\\u00E8ce\nItem.Date           = Date\nItem.EFT            = Virement \\u00E9lectronique\nItem.Memo           = Notes\nItem.NextNum        = N\\u00B0 suivant\nItem.Payee          = B\\u00E9n\\u00E9ficiaire\nItem.Trans          = Vir.\n\nLabel.AccentColor         = Accent Couleur:\nLabel.Account             = Compte:\nLabel.AccountCode         = Code client:\nLabel.AccountNumber       = Num\\u00E9ro de compte:\nLabel.AccountOptions      = Options du compte:\nLabel.AccountSeparator    = S\\u00E9parateur de comptes:\nLabel.AccountStatus       = Situation du compte:\nLabel.AccountType         = Type du compte:\nLabel.Action              = Op\\u00E9ration:\nLabel.Amount              = Montant:\nLabel.AnIntRate           = Taux d'int\\u00E9r\\u00EAt annuel:\nLabel.Available           = Disponible:\nLabel.Balance             = Solde:\nLabel.BankAccount         = Compte bancaire:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Compte racine:\nLabel.BaseColor           = Couleur de base:\nLabel.Bottom              = Bottom:\nLabel.By                  = Par:\nLabel.CashBalance         = Solde des liquidit\\u00E9s:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Bien:\nLabel.CompDaysPerYear     = Nombre de jours par an:\nLabel.CompPerTerm         = Nombre de p\\u00E9riodes par an:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Num\\u00E9ro:\nLabel.CreateCurr          = Cr\\u00E9er une devise personalis\\u00E9e:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Devises / Bien:\nLabel.Currencies          = Devises:\nLabel.Currency            = Currency:\nLabel.Current             = Courant:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Date:\nLabel.DateFormat          = Format de la date:\nLabel.DaysPastDue         = Nombre de jours avant la prochaine \\u00E9ch\\u00E9ance:\nLabel.DefaultCurrency     = Devise par d\\u00E9faut\nLabel.DelaySec            = Delai (secondes)\nLabel.Description         = Description:\nLabel.DestAccount         = Compte de destination:\nLabel.Difference          = Diff\\u00E9rence:\nLabel.Dividend            = Dividende:\nLabel.EndDate             = Date de fin:\nLabel.EndOn               = Fin le:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Balance finale:\nLabel.EquityAccount       = Compte de capitaux propres:\nLabel.EscrowPmi           = D\\u00E9pots, hypoth\\u00E8ques, etc:\nLabel.Event               = Event:\nLabel.Every               = Chaque:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Taux de change:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Frais:\nLabel.FeesAccount         = Comptes de frais:\nLabel.FileName            = Nom de fichier:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Date du premier paiement:\nLabel.FocusColor          = Concentrez Couleur:\nLabel.Format              = Format:\nLabel.Frequency           = Fr\\u00E9quence:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gains:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Hauteur:\nLabel.High                = Haut:\nLabel.Host                = H\\u00F4te:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Income Account:\nLabel.InterestAccount     = Compte d'int\\u00E9r\\u00EAt:\nLabel.LastOccurrence      = Derni\\u00E8re occurence:\nLabel.Layout              = Disposition:\nLabel.Left                = Left:\nLabel.LoanTerm            = Dur\\u00E9e de l'emprunt (mois):\nLabel.Low                 = Bas:\nLabel.MarketValue         = Valeur du march\\u00E9:\nLabel.MaxBackupCount      = Maximum number of backup files to keep:\nLabel.Memo                = Notes:\nLabel.Monospace           = Monospace:\nLabel.Name                = Nom:\nLabel.NetIncome           = Revenu net:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Prochaine date de paiement:\nLabel.Notes               = Notes:\nLabel.NumTrans            = Nombre de transactions:\nLabel.Number              = Num\\u00E9ro:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date::\nLabel.OpeningBalance      = Solde initial:\nLabel.OrigLoanAmt         = Montant d'origine de l'emprunt:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Mot de passe:\nLabel.Path                = Chemin\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Paiements par an:\nLabel.Payee               = B\\u00E9n\\u00E9ficiaire:\nLabel.Period              = Period\nLabel.Port                = Port:\nLabel.Prefix              = Pr\\u00E9fixe:\nLabel.Price               = Prix:\nLabel.Proportional        = Proportionel:\nLabel.Quantity            = Quantit\\u00E9:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Solde rapproch\\u00E9:\nLabel.RemindLater         = Me rappeler apr\\u00E8s:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = R\\u00E9p\\u00E9ter le:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = R\\u00E9solution:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Echelle:\nLabel.Securities          = Titres:\nLabel.Security            = Titre:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Show Empty Accounts\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Date de d\\u00E9but:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Position initiale:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Date du relev\\u00E9:\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = Suffixe:\nLabel.Symbol              = Symbole:\nLabel.TargetBalance       = Solde cible:\nLabel.Top                 = Top:\nLabel.Total               = Total:\nLabel.Transaction         = Transaction:\nLabel.TransferFrom        = Transfert de:\nLabel.TransferTo          = Transf\\u00E9rer vers:\nLabel.Type                = Type:\nLabel.Units               = Units:\nLabel.UserName            = Nom de l'utilisateur:\nLabel.Value               = Value:\nLabel.Verify              = V\\u00E9rification du mot de passe:\nLabel.VerifyPassword      = Verify Password:\nLabel.Visible             = Visible\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = _A propos\\u2026\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = Compte\nMenu.AccountRegister.Name             = Registre des comptes:\nMenu.AddRemoveCurrency.Name=_Ajout / Suppression\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Actualiser les devises\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = Mettre \\u00E0 jour les titres\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Point\\u00E9\nMenu.Close.Name                       = _Fermer\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = C_opier\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _Devises\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Par _d\\u00E9faut...\nMenu.DefaultCurrency.Tooltip          = Change the default currency\nMenu.Delete.Name                      = Supprimer\nMenu.Duplicate.Name                   = Dupliquer\nMenu.Edit.Name                        = _Edition\nMenu.EditTranNumList.Name             = Edit transaction number list\nMenu.Exit.Name                        = Quitt_er\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = _Exporter\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archiver...\nMenu.File.Name                        = _Fichier\nMenu.Filter.Name                      = _Filtres\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Aide\nMenu.Hide.Name                        = Masquer\nMenu.HistoryChart.Name                = Graphique historique...\nMenu.HistoryCommodity.Name            = _Historique...\nMenu.HistoryImport.Name               = Importer l'historique...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Graphique des revenus et d\\u00E9penses...\nMenu.Import.Name                      = _Importer\nMenu.ImportAccounts.Name              = Import Accounts...\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Import jGnash...\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF...\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = Liste des comptes\\u2026\nMenu.Locale.Name                      = Changer les param\\u00E8tres r\\u00E9gionaux...\nMenu.LookAndFeel.Name                 = Apparence\nMenu.MarkAs.Name                      = Mark As\nMenu.Modify.Name                      = Modifier\nMenu.ModifyCommodity.Name             = _Cr\\u00E9ation / Modification ...\nMenu.ModifyCurrency.Name              = _Modifier...\nMenu.ModifyExchangeRates.Name         = Edit Exchange Rates...\nMenu.MonthBalance.Name                = Solde mensuel...\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = Solde fin de mois...\nMenu.MonthEndBalanceCSV.Name          = Solde fin de mois (CSV)...\nMenu.NetWorth.Name                    = Net Worth...\nMenu.New.Name                         = _Nouveau\\u2026\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Ouvrir\\u2026\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = _Options...\nMenu.Option.Tooltip                   = Changer les options du programme\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = P\\u00E9riodique Solde du compte\\u2026\nMenu.Portfolio.Name                   = Portfolio...\nMenu.ProfitLoss.Name                  = Profits et pertes...\nMenu.ProfitLossTXT.Name               = Profits et pertes (texte)...\nMenu.Reconcile.Name                   = Reconcilier\nMenu.Reconciled.Name                  = Reconciled\nMenu.RecurringList.Name               = Ech\\u00E9ancier...\nMenu.Register.Name                    = Op\\u00E9_rations...\nMenu.Reports.Name                     = _Rapports\nMenu.RunJavaScript.Name               = Run JavaScript...\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = Enregi_strer\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = Enregistrer sous...\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = Bien\nMenu.SetPassword.Name                 = D\\u00E9finir le mot de passe...\nMenu.Show.Name                        = Montrer\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Th\\u00E8mes\nMenu.Tools.Name                       = _Outils\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Unreconciled\nMenu.View.Name                        = _Affichage\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = J'ai lu, compris et accepte les clauses des licences.\nMessage.AccountAdd                   = Compte ajout\\u00E9\nMessage.AccountCode                  = Le code du compte n'est pas unique: le code n'a pas \\u00E9t\\u00E9 chang\\u00E9\nMessage.AccountLocked                = Compte verrouill\\u00E9\nMessage.AccountModify                = Compte modifi\\u00E9\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = Compte supprim\\u00E9\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = La sauvegarde automatique a \\u00E9t\\u00E9 d\\u00E9sactiv\\u00E9e\nMessage.AutoSaveOn                   = La sauvegarde automatique a \\u00E9t\\u00E9 activ\\u00E9e\nMessage.CSVFile                      = Fichier au format \"champ et virgules\" (*.csv)\nMessage.CheckRecurring               = V\\u00E9rification des nouveaux \\u00E9v\\u00E9nements r\\u00E9currents\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Supprimer les transactions s\\u00E9lectionn\\u00E9es?\nMessage.ConfirmReminderDelete        = Supprimer le rappel s\\u00E9lectionn\\u00E9?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Supprimer la transaction s\\u00E9lectionn\\u00E9e?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Devise par d\\u00E9faut chang\\u00E9e en:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Moteur d\\u00E9marr\\u00E9\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Impossible de cr\\u00E9er le compte\nMessage.Error.AccountRemove          = Impossible d'enlever le compte\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Impossible d'ajouter le produit\nMessage.Error.AddCurrency            = Impossible d'ajouter la devise\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Cr\\u00E9ez d'abord un compte\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = Fichier non trouv\\u00E9\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = La licence n'a pas \\u00E9t\\u00E9 accept\\u00E9e\nMessage.Error.LoadingFile            = Erreurs durant le chargement du fichier\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Impossible de modifier le produit\nMessage.Error.ModifyCurrency         = Impossible de modifier la devise\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Aucune transaction a \\u00E9t\\u00E9 trait\\u00E9e\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Fichier ferm\\u00E9\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = Ne peut pas charger la d\\u00E9finition de rapport\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Param\\u00E8tres r\\u00E9gionaux chang\\u00E9s en:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Ne se r\\u00E9p\\u00E9tera plus\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Echec durant  l'analyse de la d\\u00E9fintion de rapport\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = Pr\\u00E9paration pour fermeture\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Les modules de rapport ont \\u00E9t\\u00E9 correctement charg\\u00E9s\nMessage.ReportWait                   = Merci de patienter, chargement des rapports\nMessage.RestartLocale                = Relancer le programme pour la prise en compte les nouveaux param\\u00E8tres r\\u00E9gionaux\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = Entrer les dates de d\\u00E9but et de fin\nMessage.StoreBackup                  = Destination de la sauvegarde:\nMessage.StoreComplete                = Fichier sauv\\u00E9\nMessage.StoreWait                    = Sauvergarde en cours\nMessage.TXTFile                      = Texte seulement (*.txt)\nMessage.TransactionAccountLocked     = L'ajout de la transaction est un \\u00E9chec, le ou les comptes destinaires sont verrouill\\u00E9s\nMessage.TransactionAdd               = Transaction ajout\\u00E9e\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transaction supprim\\u00E9e\nMessage.TransactionRemoveLocked      = La suppression de la transaction est un \\u00E9chec, le ou les comptes destinaires sont verrouill\\u00E9s\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = Produit utilis\\u00E9\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Devise utilis\\u00E9e\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Comptes bancaires\nName.ExpenseAccounts = Comptes de d\\u00E9penses\nName.IncomeAccounts  = Comptes de revenus\nName.Root            = Racine\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Du {0, date, long} au {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Page {0} of {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minutes\nPeriod.15Min     = 15 Minutes\nPeriod.1Day      = 1 jour\nPeriod.1Hr       = 1 heure\nPeriod.2Hr       = 2 heures\nPeriod.30Min     = 30 minutes\nPeriod.5Min      = 5 minutes\nPeriod.8Hr       = 8 hours\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Quotidien\nPeriod.Monthly   = Mensuel\nPeriod.NextStart = La prochaine fois jGnash red\\u00E9marre\nPeriod.None      = Aucun\nPeriod.OnlyOnce  = Une seule fois\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Hebdomadaire\nPeriod.Yearly    = Annuel\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = A propos\nTab.Accounts        = Accounts\nTab.Adjust          = Ajuster\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Charge\nTab.Credit          = Cr\\u00E9dit\nTab.Credits         = Cr\\u00E9dits\nTab.DataEngine      = Moteur de donn\\u00E9es\nTab.DataProviders   = Data Providers\nTab.Day             = Jours\nTab.Debit           = D\\u00E9bit\nTab.Formats         = Formats\nTab.GPLLicense      = Licence GPL\nTab.General         = G\\u00E9n\\u00E9ral\nTab.LGPLLicense     = Licence LGPL\nTab.License         = Licence\nTab.Month           = Mois\nTab.Network         = Network\nTab.None            = Rien\nTab.Payment         = Paiement\nTab.Register        = Op\\u00E9rations\nTab.Reminders       = Rappels\nTab.Report          = Report\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = Informations syst\\u00E8me\nTab.Transfer        = Transf\\u00E9rer\nTab.Week            = Semaine\nTab.Year            = Ann\\u00E9e\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = A propos...\nTitle.AccountBalance             = Solde de comptes\nTitle.AccountFilter              = Filtre des comptes\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Information sur le compte\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Comptes de titres\nTitle.AddRemCurr                 = Ajout / Suppression de devises\nTitle.AmortizationSetup          = Paramu00E9trage de l'ammortissement\nTitle.Archive                    = Archiver\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Sauvegarde automatique\nTitle.Available                  = Disponible\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Emplacement de sauvegarde\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Mise en page de ch\\u00E8ques\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Visibilit\\u00E9 des colonnes\nTitle.Colors                     = Couleurs\nTitle.CommoditiesSecurities      = Titres\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirmer\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Cr\\u00E9ation / Modification de biens\nTitle.Credits                    = Cr\\u00E9dits\nTitle.Currencies                 = Devises\nTitle.Current                    = Actuel\nTitle.CutOffDate                 = S\\u00E9lectionner la date limite\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = D\\u00E9bit\nTitle.DefDefCurr                 = D\\u00E9finir la devise par d\\u00E9faut\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Affichage\nTitle.DuplicateTransaction       = Duplication de transactions\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = Balance de fin de mois\nTitle.EnterPassword              = Entrer le mot de passe\nTitle.Entry                      = Entr\\u00E9e\nTitle.Error                      = Erreur\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Taux de change:\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filtres\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Fr\\u00E9quence\nTitle.HTTPProxy                  = Proxy HTTP\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Import de l'historique\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Import partiel d'un fichier QIF\nTitle.ImpSum                     = R\\u00E9sum\\u00E9 de l'importation\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = informations\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Modifier les transactions QIF\nTitle.ModifyAccount              = Modification de compte\nTitle.ModifyCurrencies           = Modification des devises\nTitle.ModifyReminder             = Modification de rappel\nTitle.ModifySecHistory           = Modification de l'historique des titres\nTitle.ModifyTransaction          = Modification de transaction\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nouveau compte\nTitle.NewBudget                  = Nouveau Budget\nTitle.NewFile                    = Nouveau fichier\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nouveau rappel\nTitle.NewTrans                   = Nouvelle transaction\nTitle.Notes                      = Notes\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Ouvrir\nTitle.Options                    = Options\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Mise en page\nTitle.ParentAccount              = Compte parent\nTitle.PercentDist                = Pourcentage\nTitle.PercentExpense             = R\\u00E9partition des d\\u00E9penses\nTitle.PercentIncome              = R\\u00E9partition des revenus\nTitle.PleaseWait                 = Merci de patienter\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profits et pertes\nTitle.ReconcileSettings          = Configuration de la reconciliation\nTitle.Reminder                   = Rappel\nTitle.Reminders                  = Rappels\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Save As\nTitle.SaveFile                   = Enregistrer le fichier\nTitle.SecurityHistory            = Historique du titre\nTitle.SelAccount                 = S\\u00E9lection d'un compte\nTitle.SelAvailCurr               = S\\u00E9lection des devises disponibles\nTitle.SelDate                    = S\\u00E9lection d'une date\nTitle.SelDefCurr                 = S\\u00E9lection de la devise par d\\u00E9faut\nTitle.SelDefLocale               = S\\u00E9lection des param\\u00E8tres r\\u00E9gionaux par d\\u00E9faut\nTitle.SelDestAccount             = S\\u00E9lection du compte de destination\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = S\\u00E9lection du compte de capitaux propres\nTitle.SelFile                    = S\\u00E9lection d'un fichier\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = S\\u00E9lection d'une couleur\nTitle.Selected                   = S\\u00E9lectionn\\u00E9\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaction\nTitle.Startup                    = Startup\nTitle.Steps                      = Etapes\nTitle.Success                    = Le Succ\\u00E8s\nTitle.Summary                    = R\\u00E9sum\\u00E9\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaction\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Liste des transactions\nTitle.TransactionSetup           = Transaction Setup\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visible\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Liste des comptes\nToolTip.AccountRegister                      = Liste des op\\u00E9rations\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Changer la visibilit\\u00E9 des colonnes\nToolTip.ConvertSEntry                        = Changer cette transaction en une transaction Double Entr\\u00E9e\nToolTip.DeleteAccount                        = Supprimer un compte\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filtrer les comptes\nToolTip.FilterAccounts                       = Filtrer par type de compte\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = que des nombres entiers autoris\\u00E9s\nToolTip.ModifyAccount                        = Modifier un compte\nToolTip.NewAccount                           = Nouveau compte\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = Imprimer le rapport du registre\nToolTip.ReconcileAccount                     = Rapprocher un compte\nToolTip.Reminders                            = Rappels\nToolTip.ResizeColumns                        = Redimensionner les colonnes\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Nombre de chiffres \\u00E0 la droite de la virgule\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Cr\\u00E9er une sauvegarde avec horodatage \\u00E0 la fermeture\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Ouvrir un nouveau registre de compte\n\nTransaction.AddShare        = Ajout de parts\nTransaction.BuyShare        = Achat de parts\nTransaction.Dividend        = Dividende\nTransaction.DoubleEntry     = Double entr\\u00E9e\nTransaction.MergeShare      = Part fusionn\\u00E9e\nTransaction.ReinvestDiv     = R\\u00E9investissement de dividende\nTransaction.RemoveShare     = Enlever des parts\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Vendre des parts\nTransaction.SingleEntry     = Simple entr\\u00E9e\nTransaction.Split           = R\\u00E9partir la transaction\nTransaction.SplitEntry      = Entr\\u00E9e de la transaction r\\u00E9partie\nTransaction.SplitShare      = Part r\\u00E9partie\nTransaction.TransferIn      = Transf\\u00E9rer dans\nTransaction.TransferOut     = Transf\\u00E9rer vers\n\nWord.Add             = Ajouter\nWord.All             = Tout\nWord.Balance         = Balance\nWord.Buy             = Acheter\nWord.Commodity       = Bien\nWord.Copy            = Copy\nWord.Description     = Description\nWord.Difference      = Difference\nWord.Dividend        = Dividende\nWord.Exchange        = Exchange\nWord.Fees            = Frais\nWord.GrossExpense    = D\\u00E9penses brutes\nWord.GrossIncome     = Revenus bruts\nWord.Interest        = Int\\u00E9r\\u00EAt\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = aller\nWord.Monthly         = Mensuel\nWord.Name            = Name\nWord.NetIncome       = Revenu net\nWord.NetWorth        = Net Worth\nWord.NewBudget       = New Budget\nWord.None            = Rien\nWord.Quarterly       = Trimestriel\nWord.ReInvDiv        = R\\u00E9investir les dividendes\nWord.Remove          = Supprimer\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Titre\nWord.Sell            = Vendre\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totals\nWord.Yearly          = Annuel\n\nqif = QIF...\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_it.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Bene\nAccountType.Bank             = Banca\nAccountType.Cash             = Cassa\nAccountType.Checking         = Assegni\nAccountType.Credit           = Credito\nAccountType.Equity           = Aperture\nAccountType.Expense          = Spese\nAccountType.Income           = Entrate\nAccountType.Investment       = Investimento\nAccountType.Liability        = Debiti\nAccountType.MoneyMarket      = Mercato monetario\nAccountType.Mutual           = Mutual Fund\nAccountType.Root             = Principale\nAccountType.SimpleInvestment = investimento semplice\n\nButton.AccTerms                = Usa termini contabili\nButton.Accounts                = Conti\nButton.AckSel                  = Riconosca Selezionato\nButton.Add                     = Aggiungi\nButton.AllDates                = tutte le date\nButton.Amortize                = Ammortamento\nButton.AnimationsEnabled       = Abilita effetti di animazione\nButton.AnyStatus               = qualsiasi Stato\nButton.Apply                   = Applica\nButton.AssetAccounts           = Asset Conti\nButton.AutoReconcile           = Riconciliazione Automatica Registrazioni Suddivise\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Titoli disponibili\nButton.Back                    = Indietro\nButton.BankAccounts            = Conti bancari\nButton.BudgetMgr               = Budget manager\nButton.Budgeting               = Budgeting\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Cancella\nButton.CheckForUpdates         = Verificare la presenza di nuovi aggiornamenti\nButton.CheckReminders          = Controlla i promemoria\nButton.Clear                   = Cancella\nButton.ClearAll                = Cancella tutto\nButton.Cleared                 = Liquidato\nButton.Close                   = Chiudi\nButton.Compare                 = Confrontare\nButton.ConcatenateMemos        = combinare Memo\nButton.ConfirmReminderDelete   = Conferma su Cancellazione Promemoria\nButton.ConfirmTransDelete      = Conferma su Cancellazione Registrazione\nButton.CopyToClip              = Copia in appunti\nButton.CreateTimeFile          = Crea file con data e ora in chiusura\nButton.CreditAccounts          = conti di credito\nButton.Delete                  = Elimina\nButton.DeleteAll               = Cancella Tutti\nButton.DeleteWeekends          = Elimina Fine settimana\nButton.DetailSplits            = Mostra dettagli suddivisione\nButton.Duplicate               = Duplica\nButton.Edit                    = Modifica\nButton.EnableAutoComplete      = Attiva completamento automatico\nButton.Enabled                 = Attivo\nButton.EndingBalance           = Saldo finale\nButton.Enter                   = Inserisci\nButton.EnterDaysBefore         = Inserisce automaticamente numero di giorni precedenti\nButton.ExcludeFromBudget       = Escludi dai bilanci\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Nota spese\nButton.Export                  = Esportare\nButton.ExportSpreadsheet       = Esporta foglio di calcolo\nButton.Filter                  = Filtra\nButton.Finish                  = Fine\nButton.FinishLater             = Finisci dopo\nButton.ForceDefaultCurrency    = Forza l'utilizzo di valuta di default\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = Richiede Autentificazione\nButton.Hidden                  = Nascosto\nButton.HideAccount             = Nascondi Account\nButton.HideLockedAccount       = Hide account bloccati\nButton.HidePlaceholderAccount  = conti Nascondi segnaposto\nButton.HideZeroBalance         = Hide conti saldo zero\nButton.HistoricalFill          = Fill Storico\nButton.Horizontal              = Orizzontale\nButton.IncludeSubAccounts      = Includi Sotto Conti\nButton.IncomeAccounts          = Conti entrate\nButton.IncomeAndExpense        = Entrate e uscite\nButton.Insert                  = Inserisci\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Invertire la selezione\nButton.Jump                    = Vai a\nButton.KeepFridays             = mantenere il venerd\\u00EC\nButton.KeepMondays             = mantenere il luned\\u00EC\nButton.Landscape               = Landscape\nButton.Last120Days             = Ultimi 120 giorni\nButton.Last12Months            = Ultimi 12 mesi\nButton.Last30Days              = Ultimi 30 giorni\nButton.Last60Days              = Ultimi 60 giorni\nButton.Last90Days              = Ultimi 90 giorni\nButton.LiabilityAccounts       = Conti di responsabilit\\u00E0\nButton.Locked                  = Bloccato\nButton.MatchAccountOnly        = Partita utilizzando l'account solo specifiche operazioni\nButton.MatchAllTrans           = Abbinare con tutte le transazioni\nButton.MatchCaseSensitive      = Match \\u00E8 case sensitive\nButton.Modify                  = Modifica\nButton.MonthlyBalance          = Saldo Mensile\nButton.New                     = Nuovo\nButton.NewEmpty                = Nuovo vuoto\nButton.NewHist                 = New Historical\nButton.NewPayment              = Nuovo pagamento\nButton.Next                    = Prossimo\nButton.No                      = No\nButton.NoEndDate               = Senza data fine\nButton.None                    = Nessuna\nButton.NotReconciled           = non riconciliato\nButton.Ok                      = OK\nButton.Open                    = Apri\nButton.OpenLastOnStartup       = Aprire ultimo file all'avvio\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Imposta pagina\nButton.PlaceHolder             = Titolare\nButton.Portrait                = Portrait\nButton.Print                   = Stampa\nButton.PrintSample             = Anteprima di stampa\nButton.Properties              = Propriet\\u00E0\nButton.Reconcile               = Riconcilia\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disabilita riconciliazione automatica\nButton.ReconcileIncomeExpense  = Automatically reconcile Income and Expense Accounts\nButton.Reconciled              = Movimento riconciliato\nButton.Refresh                 = Aggiorna\nButton.RegDate                 = Memorizza data ultima registrazione\nButton.Register                = Registrazione\nButton.RegisterFollowsList     = Registra seguente lista conti\nButton.RememberPassword        = Ricorda password\nButton.RemindLater             = Ricordamelo pi\\u00F9 tardi\nButton.Reminders               = Promemoria\nButton.RemoteServer            = Server Remoto\nButton.Remove                  = Rimuovi\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Ridimensiona\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Esecuzione Balance\nButton.Save                    = Salva\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Seleziona\nButton.SelectAll               = Seleziona tutto\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Visualizza tutte le materie prime\nButton.ShowEmptyAccounts       = Mostra conti a zero\nButton.ShowPercentValues       = Mostra valori percentuali\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Ripartizioni\nButton.Start                   = Inizio\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Tema abilitato\nButton.Timestamp               = Data\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Aggiorna valori valute all'avvio\nButton.UpdateOnline            = Aggiornamento online\nButton.UpdateSecuritiesStartup = Aggiorna titoli all'avvio\nButton.UseDailyRate            = Utilizza interesse giornaliero\nButton.UseEncryption           = Usa cifratura\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Usa nomi lunghi\nButton.UseProxy                = Usa proxy\nButton.UseRegexForFilter       = Utilizzare le espressioni regolari per i filtri\nButton.Vertical                = Verticale\nButton.Yes                     = S\\u00EC\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Conto\nColumn.AccountName                    = Nome conto\nColumn.Action                         = Azione\nColumn.Actual                         = Actual\nColumn.Amount                         = Importo\nColumn.Approve                        = Approve\nColumn.Balance                        = Saldo\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Onere\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = Merce\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Creditore\nColumn.Currency                       = Valute\nColumn.Date                           = Data\nColumn.Day                            = Giorno\nColumn.Debit                          = Debitore\nColumn.Decrease                       = Diminuzione\nColumn.Deposit                        = Versamento\nColumn.Description                    = Descrizione\nColumn.Due                            = Due\nColumn.Enabled                        = Abilitato\nColumn.Entries                        = Registrazioni\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Tasso di cambio\nColumn.Expense                        = Spesa\nColumn.Freq                           = Frequenza\nColumn.Gain                           = Utile\nColumn.High                           = Alto\nColumn.Income                         = Reddito\nColumn.Increase                       = Aumento\nColumn.Investment                     = Investimento\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Perdita\nColumn.Low                            = Basso\nColumn.Memo                           = Memo\nColumn.MktValue                       = Valore\nColumn.Month                          = Mese\nColumn.Num                            = Num\nColumn.Payee                          = Beneficiario\nColumn.Payment                        = Pagamento\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Prezzo\nColumn.Print                          = Stampa\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Quantit\\u00E0\nColumn.Rebate                         = Rimborso\nColumn.Receive                        = Ricevuto\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Titolo\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qta\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = Speso\nColumn.Timestamp                      = timestamp\nColumn.Total                          = Totale\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Tipo\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Prelevamento\n\nDataStoreType.Bxds = File binario\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Importo\nItem.CashDeposit    = Deposito in contanti\nItem.CashWithdrawal = Prelevamento in contanti\nItem.Date           = Data\nItem.EFT            = EFT\nItem.Memo           = Memo\nItem.NextNum        = Next #\nItem.Payee          = Beneficiario\nItem.Trans          = Trans\n\nLabel.AccentColor         = Risalto colore:\nLabel.Account             = Conto:\nLabel.AccountCode         = Codice conto:\nLabel.AccountNumber       = Numero di conto / cliente:\nLabel.AccountOptions      = Opzione conti:\nLabel.AccountSeparator    = Separatore conti:\nLabel.AccountStatus       = Stato conti:\nLabel.AccountType         = Tipo conto:\nLabel.Action              = Azione:\nLabel.Amount              = Importo:\nLabel.AnIntRate           = Tasso d\\u0092interesse annuale:\nLabel.Available           = Disponibile:\nLabel.Balance             = Saldo:\nLabel.BankAccount         = Conto corrente:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Conto base:\nLabel.BaseColor           = Colore di base:\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = Liquidit\\u00E0:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Merce:\nLabel.CompDaysPerYear     = Giorni di pagamento per anno:\nLabel.CompPerTerm         = Intervallo di pagamento annuale:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Numero:\nLabel.CreateCurr          = Creare valuta speciale:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Valute / Merce:\nLabel.Currencies          = Valute:\nLabel.Currency            = Valuta:\nLabel.Current             = Attuale:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Data:\nLabel.DateFormat          = Formato Data:\nLabel.DaysPastDue         = Days past due:\nLabel.DefaultCurrency     = Valuta predefinita:\nLabel.DelaySec            = Ritardo (secondi)\nLabel.Description         = Descrizione:\nLabel.DestAccount         = Conto destinatario:\nLabel.Difference          = Differenza:\nLabel.Dividend            = Dividendo:\nLabel.EndDate             = Data fine:\nLabel.EndOn               = End On:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Saldo finale:\nLabel.EquityAccount       = Equity Account:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Ogni:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Corso:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Commissioni:\nLabel.FeesAccount         = Conto commissioni:\nLabel.FileName            = Nome file:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Data del primo pagamento:\nLabel.FocusColor          = Concentrarsi Colore:\nLabel.Format              = Format:\nLabel.Frequency           = Frequenza:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Utili:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Altezza:\nLabel.High                = Alto:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Conto entrate:\nLabel.InterestAccount     = Conto interessi:\nLabel.LastOccurrence      = Ultima occorrenza:\nLabel.Layout              = Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = Durata del prestito (mesi):\nLabel.Low                 = Basso:\nLabel.MarketValue         = Valore di mercato:\nLabel.MaxBackupCount      = Numero di file backup da mantenere:\nLabel.Memo                = Memo:\nLabel.Monospace           = Monospace:\nLabel.Name                = Nome:\nLabel.NetIncome           = Net Income:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Prossima scadenza pagamento:\nLabel.Notes               = Note:\nLabel.NumTrans            = Numero registrazioni:\nLabel.Number              = Numero:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date:\nLabel.OpeningBalance      = Saldo iniziale:\nLabel.OrigLoanAmt         = Ammontare iniziale del prestito\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Parola chiave:\nLabel.Path                = Percorso\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Nr. pagamenti annuali:\nLabel.Payee               = Beneficiario:\nLabel.Period              = Period\nLabel.Port                = Porta:\nLabel.Prefix              = Prefisso:\nLabel.Price               = Prezzo:\nLabel.Proportional        = Proporzionale:\nLabel.Quantity            = Quantit\\u00E0:\nLabel.QuoteSource         = Fonte delle quote:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Saldo riconciliato:\nLabel.RemindLater         = Ricordamelo di nuovo dopo:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Repeat on:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = Risoluzione:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Tariffa:\nLabel.Securities          = Titoli:\nLabel.Security            = Titolo:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Mostra conti a zero\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Data inizio:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Posizione iniziale:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Data dell'estratto conto:\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = Suffisso:\nLabel.Symbol              = Simbolo:\nLabel.TargetBalance       = Obbiettivo saldo:\nLabel.Top                 = Top:\nLabel.Total               = Totale:\nLabel.Transaction         = Registrazione:\nLabel.TransferFrom        = Transfer From:\nLabel.TransferTo          = Trasferire a:\nLabel.Type                = Tipo:\nLabel.Units               = Units:\nLabel.UserName            = Nome utente:\nLabel.Value               = Value:\nLabel.Verify              = Verifica:\nLabel.VerifyPassword      = Verifica Password:\nLabel.Visible             = Visibile:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = _About\\u2026\nMenu.About.Tooltip                    = Informazioni su jGnash\nMenu.Account.Name                     = Conto\nMenu.AccountRegister.Name             = Account Register...\nMenu.AddRemoveCurrency.Name=_Aggiungi / Rimuovi\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = aggiornamento Valute\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = aggiornamento Securities\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Liquidato\nMenu.Close.Name                       = _Chiudi\nMenu.Close.Tooltip                    = Chiudi file attivo\nMenu.CloseAllWindows.Name             = Chiudi tutte le finestre\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = _Copia\nMenu.Copy.Tooltip                     = Duplica elemento selezionato\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = Val_ute\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = _Predefinito...\nMenu.DefaultCurrency.Tooltip          = Cambia valuta predefinita\nMenu.Delete.Name                      = Elimina\nMenu.Duplicate.Name                   = Duplica\nMenu.Edit.Name                        = _Modifica\nMenu.EditTranNumList.Name             = Modifica elenco numeri Registrazioni\nMenu.Exit.Name                        = _Esci\nMenu.Exit.Tooltip                     = Chiudi jGnash\nMenu.Export.Name                      = _Esporta\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archivia...\nMenu.File.Name                        = _File\nMenu.Filter.Name                      = _Filtri\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Aiuto\nMenu.Hide.Name                        = Nascondi\nMenu.HistoryChart.Name                = Grafico storico\\u2026\nMenu.HistoryCommodity.Name            = Storico\\u2026\nMenu.HistoryImport.Name               = Importa valori storici\\u2026\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Grafico a torta Entrate/Uscite\\u2026\nMenu.Import.Name                      = _Importa\nMenu.ImportAccounts.Name              = Import Accounts\\u2026\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Importa jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Importa file jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Importa file SWIFT MT940\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Importa file OFX e QFX\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = _Lista conti\\u2026\nMenu.Locale.Name                      = Cambia localit\\u00E0\\u2026\nMenu.LookAndFeel.Name                 = Aspetto grafico\nMenu.MarkAs.Name                      = Segna come\nMenu.Modify.Name                      = Modifica\nMenu.ModifyCommodity.Name             = _Crea/Modifica\\u2026\nMenu.ModifyCurrency.Name              = _Modifica\\u2026\nMenu.ModifyExchangeRates.Name         = Modifica tassi di cambio\\u2026\nMenu.MonthBalance.Name                = Bilancio mensile\\u2026\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = Bilancio di fine mese\\u2026\nMenu.MonthEndBalanceCSV.Name          = Bilancio di fine mese (CSV)\\u2026\nMenu.NetWorth.Name                    = Patrimonio netto\\u2026\nMenu.New.Name                         = _Nuovo\\u2026\nMenu.New.Tooltip                      = Crea un nuovo file\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Apri\\u2026\nMenu.Open.Tooltip                     = Apre un file specifico\nMenu.Option.Name                      = _Opzioni...\nMenu.Option.Tooltip                   = Cambia opzioni del programma\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = Saldo conto periodico\\u2026\nMenu.Portfolio.Name                   = Portfolio...\nMenu.ProfitLoss.Name                  = Profitti e perdite ...\nMenu.ProfitLossTXT.Name               = Profitti e perdite (testo) ...\nMenu.Reconcile.Name                   = Riconciliazione\nMenu.Reconciled.Name                  = Riconciliato\nMenu.RecurringList.Name               = Registrazioni ripetitive...\nMenu.Register.Name                    = _Registrazioni...\nMenu.Reports.Name                     = _Rendiconti\nMenu.RunJavaScript.Name               = Esegui JavaScript...\nMenu.RunJavaScript.Tooltip            = Esegui JavaScript\nMenu.Save.Name                        = _Salva\nMenu.Save.Tooltip                     = Salva il file corrente\nMenu.SaveAs.Name                      = Salva con nome...\nMenu.SaveAs.Tooltip                   = Salva il file corrente con un nome diverso\nMenu.Securities.Name                  = M_erce\nMenu.SetPassword.Name                 = Imposta parola chiave...\nMenu.Show.Name                        = Visualizza\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Temi\nMenu.Tools.Name                       = _Strumenti\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Unreconciled\nMenu.View.Name                        = _Visualizza\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = Ho letto, compreso e accettato i termini di licenza di utilizzo.\nMessage.AccountAdd                   = Account aggiunto\nMessage.AccountCode                  = Account code was not unique: The code was not changed\nMessage.AccountLocked                = Conto bloccato\nMessage.AccountModify                = Account modificato\nMessage.AccountMoveFailed            = Non posso muovere l'account\nMessage.AccountRemove                = Account rimosso\nMessage.AntiAlias                    = Abilita antialiasing del testo!\nMessage.AutoSaveOff                  = Salvataggio automatico disabilitato\nMessage.AutoSaveOn                   = Salvataggio automatico abilitato\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Checking for new recurring events\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Cancello le registrazioni selezionate?\nMessage.ConfirmReminderDelete        = Cancello il promemoria selezionato?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Cancello la registrazione selezionata?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Valuta predefinita cambiata in:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine started\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Non sono riuscito a creare l'account\nMessage.Error.AccountRemove          = Could not remove account\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Could not add commodity\nMessage.Error.AddCurrency            = Could not add currency\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Create a basic account set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = File non trovato\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = La licenza non e' stata accettata\nMessage.Error.LoadingFile            = Error loading file\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Could not modify commodity\nMessage.Error.ModifyCurrency         = Could not modify currency\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = File chiuso\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = Il file selezionato non \\u00E8 valido\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash richiede una JVM 11 o superiore\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Localit\\u00E0 predefinita cambiata in:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = Il database esistente verr\\u00E0 sovrascritto\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Attendere prego ...\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = Preparazione alla sospensione\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Report modules were loaded successfully\nMessage.ReportWait                   = Please wait, Loading report modules\nMessage.RestartLocale                = Riavviare per applicare il cambiamento di localit\\u00E0\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = Enter starting and ending dates\nMessage.StoreBackup                  = Saving backup to:\nMessage.StoreComplete                = File store is complete\nMessage.StoreWait                    = Waiting for file store to complete\nMessage.TXTFile                      = File di testo (*.txt)\nMessage.TransactionAccountLocked     = Transaction add failed, Destination account(s) are locked\nMessage.TransactionAdd               = Registrazione inserita\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Registrazione rimossa\nMessage.TransactionRemoveLocked      = Transaction removal failed, Destination account(s) are locked\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = Versione in uso\nMessage.Warn.CommodityInUse          = Commodity is in use\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Currency is in use\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bank Accounts\nName.ExpenseAccounts = Expense Accounts\nName.IncomeAccounts  = Income Accounts\nName.Root            = Radice\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Da {0,date,long} A {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Pagina {0} di {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minuti\nPeriod.15Min     = 15 Minuti\nPeriod.1Day      = 1 Giorno\nPeriod.1Hr       = 1 Ora\nPeriod.2Hr       = 2 Ore\nPeriod.30Min     = 30 Minuti\nPeriod.5Min      = 5 Minuti\nPeriod.8Hr       = 8 Ore\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Giornaliera\nPeriod.Monthly   = Mensile\nPeriod.NextStart = Al prossimo avvio di jGnash\nPeriod.None      = Mai\nPeriod.OnlyOnce  = Una volta sola\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Settimanale\nPeriod.Yearly    = Annuale\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = Nessuno\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = About\nTab.Accounts        = Accounts\nTab.Adjust          = Arrotondamento\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Addebito\nTab.Credit          = Creditore\nTab.Credits         = Crediti\nTab.DataEngine      = Motore dati\nTab.DataProviders   = Data Providers\nTab.Day             = Giorno\nTab.Debit           = Debitore\nTab.Formats         = Formats\nTab.GPLLicense      = Licenza GPL\nTab.General         = Generale\nTab.LGPLLicense     = Licenza LGPL\nTab.License         = Licenza\nTab.Month           = Mese\nTab.Network         = Network\nTab.None            = Nessuno\nTab.Payment         = Pagamento\nTab.Register        = Registra\nTab.Reminders       = Promemoria\nTab.Report          = Report\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = System Information\nTab.Transfer        = Trasferimento\nTab.Week            = Settimana\nTab.Year            = Anno\n\nTag.Bank                   = Banca\nTag.Dividend               = Dividendo\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Utili/(Perdite)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = A proposito di...\nTitle.AccountBalance             = Account Balance\nTitle.AccountFilter              = Filtra conti\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Informazioni sul conto\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Conto titoli\nTitle.AddRemCurr                 = Aggiungi/Rimuovi valute\nTitle.AmortizationSetup          = Piano di ammortamento\nTitle.Archive                    = Archive\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Salvataggio automatico\nTitle.Available                  = Disponibile\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Accantonamento\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Check Designer\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Visibilit\\u00E0 colonna\nTitle.Colors                     = Colori\nTitle.CommoditiesSecurities      = Titoli\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Conferma\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Crea/Modifica titoli\nTitle.Credits                    = Avere\nTitle.Currencies                 = Valuta\nTitle.Current                    = Attuale\nTitle.CutOffDate                 = Select Cut Off Date\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Dare\nTitle.DefDefCurr                 = Usa valuta predefinita:\nTitle.DefTranNum                 = Numeri predefiniti registrazione\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Visualizza\nTitle.DuplicateTransaction       = Duplica registrazione\nTitle.DuplicateTransactionsFound = Trovata registrazione doppia\nTitle.EditExchangeRates          = Modifica tassi di cambio\nTitle.EndMonthBalance            = Fine mese saldo del conto\nTitle.EnterPassword              = Inserire la password\nTitle.Entry                      = Entry\nTitle.Error                      = Errore\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Tasso di cambio\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filtri\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Frequency\nTitle.HTTPProxy                  = HTTP proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Importa valori storici\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Importa un file QIF parziale\nTitle.ImpSum                     = Riassunto importi\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Importa registrazioni da file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = informazioni\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modifica Registrazioni\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Modifica transazioni QIF\nTitle.ModifyAccount              = Modifica conto\nTitle.ModifyCurrencies           = Modifica valuta\nTitle.ModifyReminder             = Modifica promemoria\nTitle.ModifySecHistory           = Modifica lista titoli\nTitle.ModifyTransaction          = Modifica Registrazione\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nuovo Conto\nTitle.NewBudget                  = Nuovo Bilancio\nTitle.NewFile                    = Nuovo File\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nuovo Promemoria\nTitle.NewTrans                   = Nuova Registrazione\nTitle.Notes                      = Note\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Apri\nTitle.Options                    = Opzioni\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Imposta pagina\nTitle.ParentAccount              = Conto Principale\nTitle.PercentDist                = Percent Distribution\nTitle.PercentExpense             = Percent Expense\nTitle.PercentIncome              = Percent Income\nTitle.PleaseWait                 = Please Wait\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profit and Loss Statement\nTitle.ReconcileSettings          = Setup riconciliazione:\nTitle.Reminder                   = Promemoria\nTitle.Reminders                  = Promemoria\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Salva come\nTitle.SaveFile                   = Salva File\nTitle.SecurityHistory            = Storico titoli\nTitle.SelAccount                 = Select Account\nTitle.SelAvailCurr               = Seleziona valute disponibili\nTitle.SelDate                    = Select Date\nTitle.SelDefCurr                 = Imposta valuta predefinita\nTitle.SelDefLocale               = Imposta localit\\u00E0 predefinita\nTitle.SelDestAccount             = Selezione conto destinatario\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Select Equity Account\nTitle.SelFile                    = Select File\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = Seleziona colore\nTitle.Selected                   = Selezionato\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Suddividi Registrazione\nTitle.Startup                    = Startup\nTitle.Steps                      = Passi\nTitle.Success                    = Successo\nTitle.Summary                    = Riassunto\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Registrazione\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Lista Registrazioni\nTitle.TransactionSetup           = Imposta registrazioni\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visibile\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Lista conti\nToolTip.AccountRegister                      = Registrazione conto\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Change column visibility\nToolTip.ConvertSEntry                        = Change this transaction into a Double Entry transaction\nToolTip.DeleteAccount                        = Elimina conto\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filtra conto\nToolTip.FilterAccounts                       = Filtra conti per tipo\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = ammessi solo numeri interi\nToolTip.ModifyAccount                        = Modifica conto\nToolTip.NewAccount                           = Nuovo conto\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = Print register report\nToolTip.ReconcileAccount                     = Riconciliazione Conto/Movimenti\nToolTip.Reminders                            = Promemoria\nToolTip.ResizeColumns                        = Ridimensiona colonne\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Numero di decimali da usare\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Crea un backup con data del giorno\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Open a new account register\n\nTransaction.AddShare        = Aggiungi titoli\nTransaction.BuyShare        = Compera titolo\nTransaction.Dividend        = Dividendo\nTransaction.DoubleEntry     = Partita doppia\nTransaction.MergeShare      = Raggruppamento titoli\nTransaction.ReinvestDiv     = Reinvestire Dividendo\nTransaction.RemoveShare     = Cancellazione Titoli\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Vendita titolo\nTransaction.SingleEntry     = Partita singola\nTransaction.Split           = Registrazione suddivisa\nTransaction.SplitEntry      = Suddivisione singola registrazione\nTransaction.SplitShare      = Split del titolo\nTransaction.TransferIn      = Trasferimento da\nTransaction.TransferOut     = Trasferimento a\n\nWord.Add             = Aggiungi\nWord.All             = Tutto\nWord.Balance         = Bilancio\nWord.Buy             = Acquista\nWord.Commodity       = Merce\nWord.Copy            = Copy\nWord.Description     = Descrizione\nWord.Difference      = Differenza\nWord.Dividend        = Dividendo\nWord.Exchange        = Cambio\nWord.Fees            = Commissioni\nWord.GrossExpense    = Gross Expense\nWord.GrossIncome     = Gross Income\nWord.Interest        = Interessi\nWord.Into            = in\nWord.Invalid         = Non valido\nWord.Merge           = andare\nWord.Monthly         = Mensile\nWord.Name            = Nome\nWord.NetIncome       = Net Income\nWord.NetWorth        = Patrimonio netto\nWord.NewBudget       = New Budget\nWord.None            = Nessuno\nWord.Quarterly       = Trimestrale\nWord.ReInvDiv        = Reinvesti dividenti\nWord.Remove          = Rimuovi\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Titolo\nWord.Sell            = Vendi\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Totale\nWord.Totals          = Totals\nWord.Yearly          = Annuale\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_iw.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = \\u05E0\\u05B6\\u05DB\\u05B6\\u05E1\nAccountType.Bank             = \\u05D1\\u05E0\\u05E7\nAccountType.Cash             = \\u05DE\\u05D6\\u05D5\\u05DE\\u05DF\nAccountType.Checking         = Checking\nAccountType.Credit           = \\u05D6\\u05D9\\u05DB\\u05D5\\u05D9\nAccountType.Equity           = Equity\nAccountType.Expense          = \\u05D4\\u05D5\\u05E6\\u05D0\\u05D4\nAccountType.Income           = \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4\nAccountType.Investment       = \\u05D4\\u05E9\\u05E7\\u05E2\\u05D4\nAccountType.Liability        = \\u05D4\\u05EA\\u05D7\\u05D9\\u05D1\\u05D5\\u05EA\nAccountType.MoneyMarket      = \\u05E9\\u05D5\\u05E7 \\u05D4\\u05DB\\u05E1\\u05E3\nAccountType.Mutual           = \\u05E7\\u05E8\\u05DF \\u05E0\\u05D0\\u05DE\\u05E0\\u05D5\\u05EA\nAccountType.Root             = Root\nAccountType.SimpleInvestment = \\u05D4\\u05E9\\u05E7\\u05E2\\u05D4 \\u05E4\\u05E9\\u05D5\\u05D8\\u05D4\n\nButton.AccTerms                = \\u05D4\\u05E9\\u05EA\\u05DE\\u05E9 \\u05D1\\u05DE\\u05D5\\u05E0\\u05D7\\u05D9\\u05DD \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D0\\u05D9\\u05DD\nButton.Accounts                = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA\nButton.AckSel                  = \\u05D4\\u05DB\\u05E8\\u05EA \\u05E0\\u05D1\\u05D7\\u05E8\nButton.Add                     = \\u05D4\\u05D5\\u05E1\\u05E3\nButton.AllDates                = \\u05DB\\u05DC \\u05D4\\u05EA\\u05D0\\u05E8\\u05D9\\u05DB\\u05D9\\u05DD\nButton.Amortize                = \\u05E4\\u05D7\\u05EA\nButton.AnimationsEnabled       = \\u05D0\\u05E4\\u05E9\\u05E8 \\u05D0\\u05E4\\u05E7\\u05D8\\u05D9\\u05DD \\u05D5\\u05D0\\u05E0\\u05D9\\u05DE\\u05E6\\u05D9\\u05D4\nButton.AnyStatus               = \\u05DB\\u05DC \\u05E1\\u05D8\\u05D8\\u05D5\\u05E1\nButton.Apply                   = \\u05D4\\u05D7\\u05DC\nButton.AssetAccounts           = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05E0\\u05DB\\u05E1\\u05D9\\u05DD\nButton.AutoReconcile           = \\u05EA\\u05D0\\u05DD\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = \\u05D7\\u05D6\\u05D5\\u05E8\nButton.BankAccounts            = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D1\\u05E0\\u05E7\nButton.BudgetMgr               = \\u05DE\\u05E0\\u05D4\\u05DC \\u05EA\\u05E7\\u05E6\\u05D9\\u05D1\nButton.Budgeting               = \\u05EA\\u05B4\\u05E7\\u05E6\\u05D5\\u05BC\\u05D1\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = \\u05D1\\u05D8\\u05DC\nButton.CheckForUpdates         = \\u05D1\\u05D3\\u05D5\\u05E7 \\u05D0\\u05DD \\u05D9\\u05E9 \\u05E2\\u05D3\\u05DB\\u05D5\\u05E0\\u05D9\\u05DD \\u05D7\\u05D3\\u05E9\\u05D9\\u05DD\nButton.CheckReminders          = \\u05D1\\u05D3\\u05D5\\u05E7 \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05D5\\u05EA\nButton.Clear                   = \\u05E0\\u05E7\\u05D4\nButton.ClearAll                = \\u05E0\\u05E7\\u05D4 \\u05D4\\u05DB\\u05D5\\u05DC\nButton.Cleared                 = \\u05E0\\u05D5\\u05E7\\u05D4\nButton.Close                   = \\u05E1\\u05D2\\u05D5\\u05E8\nButton.Compare                 = \\u05DC\\u05B0\\u05D4\\u05B7\\u05E9\\u05C1\\u05B0\\u05D5\\u05D5\\u05B9\\u05EA\nButton.ConcatenateMemos        = Combine Memos\nButton.ConfirmReminderDelete   = \\u05D0\\u05D9\\u05E9\\u05D5\\u05E8 \\u05D1\\u05DE\\u05D7\\u05D9\\u05E7\\u05EA \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05EA\nButton.ConfirmTransDelete      = \\u05D0\\u05D9\\u05E9\\u05D5\\u05E8 \\u05D1\\u05DE\\u05D7\\u05D9\\u05E7\\u05EA \\u05E4\\u05E2\\u05D5\\u05DC\\u05D5\\u05EA\nButton.CopyToClip              = \\u05D4\\u05E2\\u05EA\\u05E7 \\u05DC\\u05DC\\u05D5\\u05D7\nButton.CreateTimeFile          = \\u05E6\\u05D5\\u05E8 \\u05E7\\u05D5\\u05D1\\u05E5 timestamped \\u05D1\\u05D9\\u05E6\\u05D9\\u05D0\\u05D4\nButton.CreditAccounts          = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D0\\u05E9\\u05E8\\u05D0\\u05D9\nButton.Delete                  = \\u05DE\\u05D7\\u05D9\\u05E7\\u05D4\nButton.DeleteAll               = \\u05DE\\u05D7\\u05E7 \\u05D4\\u05DB\\u05D5\\u05DC\nButton.DeleteWeekends          = \\u05DE\\u05D7\\u05E7 \\u05E1\\u05D5\\u05E4\\u05D9 \\u05E9\\u05D1\\u05D5\\u05E2\nButton.DetailSplits            = \\u05D4\\u05E8\\u05D0\\u05D4 \\u05E4\\u05E8\\u05D8\\u05D9 \\u05E4\\u05D9\\u05E6\\u05D5\\u05DC\nButton.Duplicate               = \\u05E9\\u05D9\\u05DB\\u05E4\\u05D5\\u05DC\nButton.Edit                    = \\u05E2\\u05E8\\u05D9\\u05DB\\u05D4\nButton.EnableAutoComplete      = \\u05D4\\u05E4\\u05E2\\u05DC \\u05D4\\u05E9\\u05DC\\u05DE\\u05D4 \\u05D0\\u05D5\\u05D8\\u05D5\\u05DE\\u05D8\\u05D9\\u05EA\nButton.Enabled                 = \\u05DE\\u05D5\\u05E4\\u05E2\\u05DC\nButton.EndingBalance           = \\u05D9\\u05EA\\u05E8\\u05EA \\u05E1\\u05D2\\u05D9\\u05E8\\u05D4\nButton.Enter                   = \\u05D4\\u05DB\\u05E0\\u05E1\nButton.EnterDaysBefore         = \\u05D4\\u05DB\\u05E0\\u05E1 \\u05D0\\u05D5\\u05D8\\u05D5\\u05DE\\u05D8\\u05D9\\u05EA \\u05DE\\u05E1\\u05E4\\u05E8 \\u05D9\\u05DE\\u05D9\\u05DD \\u05DC\\u05E4\\u05E0\\u05D9\nButton.ExcludeFromBudget       = \\u05D0\\u05DC \\u05EA\\u05DB\\u05DC\\u05D5\\u05DC \\u05DE\\u05EA\\u05E7\\u05E6\\u05D9\\u05D1\\u05D9\\u05DD\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D4\\u05D5\\u05E6\\u05D0\\u05D4\nButton.Export                  = \\u05D9\\u05B0\\u05E6\\u05D5\\u05BC\\u05D0\nButton.ExportSpreadsheet       = \\u05D2\\u05D9\\u05DC\\u05D9\\u05D5\\u05DF \\u05D0\\u05DC\\u05E7\\u05D8\\u05E8\\u05D5\\u05E0\\u05D9 \\u05D9\\u05E6\\u05D5\\u05D0\nButton.Filter                  = \\u05E1\\u05E0\\u05DF\nButton.Finish                  = \\u05E1\\u05D9\\u05D9\\u05DD\nButton.FinishLater             = \\u05E1\\u05D9\\u05D9\\u05DD \\u05D0\\u05D7\"\\u05DB\nButton.ForceDefaultCurrency    = \\u05E9\\u05D9\\u05DE\\u05D5\\u05E9 \\u05D1\\u05DB\\u05D5\\u05D7 \\u05E9\\u05DC \\u05DE\\u05D8\\u05D1\\u05E2 \\u05D1\\u05E8\\u05D9\\u05E8\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = \\u05E0\\u05D3\\u05E8\\u05E9 \\u05D6\\u05D9\\u05D4\\u05D5\\u05D9\nButton.Hidden                  = \\u05DE\\u05D5\\u05E1\\u05EA\\u05E8\nButton.HideAccount             = \\u05D4\\u05E1\\u05EA\\u05E8 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nButton.HideLockedAccount       = \\u05D4\\u05E1\\u05EA\\u05E8 \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05E0\\u05E2\\u05D5\\u05DC\\u05D9\\u05DD\nButton.HidePlaceholderAccount  = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05DE\\u05E6\\u05D9\\u05D9\\u05DF \\u05DE\\u05D9\\u05E7\\u05D5\\u05DD \\u05D4\\u05E1\\u05EA\\u05E8\nButton.HideZeroBalance         = \\u05D4\\u05E1\\u05EA\\u05E8 \\u05D0\\u05E4\\u05E1 \\u05D1\\u05D9\\u05EA\\u05E8\\u05EA\nButton.HistoricalFill          = \\u05DE\\u05D9\\u05DC\\u05D5\\u05D9 \\u05D4\\u05D9\\u05E1\\u05D8\\u05D5\\u05E8\\u05D9\nButton.Horizontal              = \\u05D0\\u05D5\\u05E4\\u05E7\\u05D9\nButton.IncludeSubAccounts      = \\u05DB\\u05D5\\u05DC\\u05DC\\u05D9\\u05DD \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA Sub\nButton.IncomeAccounts          = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4\nButton.IncomeAndExpense        = \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4 \\u05D5\\u05D4\\u05D5\\u05E6\\u05D0\\u05D4\nButton.Insert                  = \\u05D4\\u05DB\\u05E0\\u05E1\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = \\u05D4\\u05E4\\u05D5\\u05DA \\u05D1\\u05D7\\u05D9\\u05E8\\u05D4\nButton.Jump                    = \\u05E2\\u05D1\\u05D5\\u05E8\nButton.KeepFridays             = \\u05E9\\u05DE\\u05D5\\u05E8 \\u05E9\\u05D9\\u05E9\\u05D9\nButton.KeepMondays             = \\u05E9\\u05DE\\u05D5\\u05E8 \\u05E9\\u05E0\\u05D9\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u05DC\\u05D0\\u05D7\\u05E8\\u05D5\\u05E0\\u05D4 120 \\u05D9\\u05DE\\u05D9\\u05DD\nButton.Last12Months            = 12 \\u05D7\\u05D5\\u05D3\\u05E9\\u05D9\\u05DD \\u05D4\\u05D0\\u05D7\\u05E8\\u05D5\\u05E0\\u05D9\\u05DD\nButton.Last30Days              = 30 \\u05D4\\u05D9\\u05DE\\u05D9\\u05DD \\u05D4\\u05D0\\u05D7\\u05E8\\u05D5\\u05E0\\u05D9\\u05DD\nButton.Last60Days              = 60 \\u05D4\\u05D9\\u05DE\\u05D9\\u05DD \\u05D4\\u05D0\\u05D7\\u05E8\\u05D5\\u05E0\\u05D9\\u05DD\nButton.Last90Days              = 90 \\u05D4\\u05D9\\u05DE\\u05D9\\u05DD \\u05D4\\u05D0\\u05D7\\u05E8\\u05D5\\u05E0\\u05D9\\u05DD\nButton.LiabilityAccounts       = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D0\\u05D7\\u05E8\\u05D9\\u05D5\\u05EA\nButton.Locked                  = \\u05E0\\u05E2\\u05D5\\u05DC\nButton.MatchAccountOnly        = \\u05D4\\u05EA\\u05D0\\u05DE\\u05D4 \\u05D1\\u05D0\\u05DE\\u05E6\\u05E2\\u05D5\\u05EA \\u05E8\\u05E7 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05E2\\u05E1\\u05E7\\u05D0\\u05D5\\u05EA \\u05E1\\u05E4\\u05E6\\u05D9\\u05E4\\u05D9\\u05D5\\u05EA\nButton.MatchAllTrans           = \\u05D4\\u05EA\\u05D0\\u05DD \\u05D1\\u05D0\\u05DE\\u05E6\\u05E2\\u05D5\\u05EA \\u05DB\\u05DC \\u05D4\\u05E2\\u05E1\\u05E7\\u05D0\\u05D5\\u05EA\nButton.MatchCaseSensitive      = \\u05D4\\u05EA\\u05D0\\u05DE\\u05EA Caps Lock \\u05D1\\u05DE\\u05D9\\u05D3\\u05EA \\u05D4\\u05E6\\u05D5\\u05E8\\u05DA\nButton.Modify                  = \\u05E9\\u05E0\\u05D4\nButton.MonthlyBalance          = \\u05D9\\u05EA\\u05E8\\u05D4 \\u05D7\\u05D5\\u05D3\\u05E9\\u05D9\\u05EA\nButton.New                     = \\u05D7\\u05D3\\u05E9\nButton.NewEmpty                = \\u05D7\\u05D3\\u05E9 \\u05E8\\u05D9\\u05E7\nButton.NewHist                 = New Historical\nButton.NewPayment              = \\u05EA\\u05E9\\u05DC\\u05D5\\u05DD \\u05D7\\u05D3\\u05E9\nButton.Next                    = \\u05D4\\u05D1\\u05D0\nButton.No                      = \\u05DC\\u05D0\nButton.NoEndDate               = \\u05DC\\u05DC\\u05D0 \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05E1\\u05D9\\u05D5\\u05DD\nButton.None                    = \\u05D0\\u05E3 \\u05DC\\u05D0 \\u05D0\\u05D7\\u05D3\nButton.NotReconciled           = \\u05DC\\u05D0 \\u05D4\\u05E9\\u05DC\\u05D9\\u05DD\nButton.Ok                      = \\u05D0\\u05D9\\u05E9\\u05D5\\u05E8\nButton.Open                    = \\u05E4\\u05EA\\u05D9\\u05D7\\u05D4\nButton.OpenLastOnStartup       = \\u05D4\\u05E7\\u05D5\\u05D1\\u05E5 \\u05D4\\u05D0\\u05D7\\u05E8\\u05D5\\u05DF \\u05DC\\u05D4\\u05E8\\u05D7\\u05D9\\u05D1 \\u05D1\\u05D4\\u05E4\\u05E2\\u05DC\\u05D4\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = \\u05D4\\u05D2\\u05D3\\u05E8\\u05EA \\u05E2\\u05DE\\u05D5\\u05D3\nButton.PlaceHolder             = Placeholder\nButton.Portrait                = Portrait\nButton.Print                   = \\u05D4\\u05D3\\u05E4\\u05E1\\u05D4\nButton.PrintSample             = \\u05D4\\u05D3\\u05E4\\u05E1 \\u05D3\\u05D5\\u05D2\\u05DE\\u05D4\nButton.Properties              = \\u05E0\\u05DB\\u05E1\\u05D9\\u05DD\nButton.Reconcile               = \\u05EA\\u05D0\\u05DD\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disable automatic reconciliation\nButton.ReconcileIncomeExpense  = automatically reconcile Income and Expense Accounts\nButton.Reconciled              = \\u05DE\\u05EA\\u05D5\\u05D0\\u05DD\nButton.Refresh                 = \\u05E8\\u05E2\\u05E0\\u05DF\nButton.RegDate                 = \\u05D6\\u05DB\\u05D5\\u05E8 \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05E9\\u05DC \\u05E4\\u05E2\\u05D5\\u05DC\\u05D4 \\u05D0\\u05D7\\u05E8\\u05D5\\u05E0\\u05D4\nButton.Register                = Register\nButton.RegisterFollowsList     = Register Follows Account List\nButton.RememberPassword        = Remember Password\nButton.RemindLater             = \\u05D4\\u05D6\\u05DB\\u05E8 \\u05DE\\u05D0\\u05D5\\u05D7\\u05E8 \\u05D9\\u05D5\\u05EA\\u05E8\nButton.Reminders               = \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05D5\\u05EA\nButton.RemoteServer            = Remote Server\nButton.Remove                  = \\u05D4\\u05E1\\u05E8\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = \\u05E9\\u05E0\\u05D4 \\u05D2\\u05D5\\u05D3\\u05DC\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = \\u05D9\\u05EA\\u05E8\\u05D4 \\u05E4\\u05E2\\u05D9\\u05DC\\u05D4\nButton.Save                    = \\\\u05E9\\\\u05DE\\\\u05D9\\\\u05E8\\\\u05D4\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = \\u05D1\\u05D7\\u05E8\nButton.SelectAll               = \\u05D1\\u05D7\\u05E8 \\u05D4\\u05DB\\u05D5\\u05DC\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = \\u05D4\\u05E8\\u05D0\\u05D4 \\u05D0\\u05EA \\u05DB\\u05DC \\u05D4\\u05E1\\u05D7\\u05D5\\u05E8\\u05D5\\u05EA\nButton.ShowEmptyAccounts       = \\u05D4\\u05E8\\u05D0\\u05D4 \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05DE\\u05D0\\u05D5\\u05E4\\u05E1\\u05D9\\u05DD\nButton.ShowPercentValues       = \\u05D4\\u05E8\\u05D0\\u05D4 \\u05E2\\u05E8\\u05DB\\u05D9\\u05DD \\u05D1\\u05D0\\u05D7\\u05D5\\u05D6\\u05D9\\u05DD\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = \\u05E4\\u05D9\\u05E6\\u05D5\\u05DC\\u05D9\\u05DD\nButton.Start                   = \\u05D4\\u05EA\\u05D7\\u05DC\nButton.Stop                    = \\u05E2\\u05E6\\u05D5\\u05E8\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Themes Enabled\nButton.Timestamp               = \\u05D7\\u05EA\\u05D9\\u05DE\\u05EA \\u05D6\\u05DE\\u05DF\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Update currency exchange rates on startup\nButton.UpdateOnline            = \\u05E2\\u05D3\\u05DB\\u05DF \\u05D0\\u05D5\\u05E0\\u05DC\\u05D9\\u05D9\\u05DF\nButton.UpdateSecuritiesStartup = Update securities on startup\nButton.UseDailyRate            = \\u05D4\\u05E9\\u05EA\\u05DE\\u05E9 \\u05D1\\u05EA\\u05E2\\u05E8\\u05D9\\u05E3 \\u05D9\\u05D5\\u05DE\\u05D9\nButton.UseEncryption           = \\u05D4\\u05E6\\u05E4\\u05E0\\u05D4\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = \\u05D1\\u05E9\\u05DE\\u05D5\\u05EA \\u05D0\\u05E8\\u05D5\\u05DB\\u05D9\\u05DD\nButton.UseProxy                = \\u05E9\\u05D9\\u05DE\\u05D5\\u05E9 \\u05D1\\u05E4\\u05E8\\u05D5\\u05E7\\u05E1\\u05D9\nButton.UseRegexForFilter       = \\u05D4\\u05E9\\u05EA\\u05DE\\u05E9 \\u05D1\\u05D1\\u05D9\\u05D8\\u05D5\\u05D9\\u05D9\\u05DD \\u05E8\\u05D2\\u05D9\\u05DC\\u05D9\\u05DD \\u05DC\\u05DE\\u05E1\\u05E0\\u05E0\\u05D9\\u05DD\nButton.Vertical                = \\u05D0\\u05E0\\u05DB\\u05D9\nButton.Yes                     = \\u05DB\\u05DF\nButton.Zoom                    = \\u05D6\\u05D5\\u05DD\n\nColumn.Account                        = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nColumn.AccountName                    = \\u05E9\\u05DD \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nColumn.Action                         = Action\nColumn.Actual                         = Actual\nColumn.Amount                         = \\u05E1\\u05DB\\u05D5\\u05DD\nColumn.Approve                        = \\u05D0\\u05D9\\u05E9\\u05D5\\u05E8\nColumn.Balance                        = \\u05D9\\u05EA\\u05E8\\u05D4\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = \\u05D7\\u05D9\\u05D5\\u05D1\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = \\u05D6\\u05DB\\u05D5\\u05EA\nColumn.Currency                       = \\u05DE\\u05D8\\u05D1\\u05E2\nColumn.Date                           = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA\nColumn.Day                            = \\u05D9\\u05D5\\u05DD\nColumn.Debit                          = \\u05D7\\u05D5\\u05D1\\u05D4\nColumn.Decrease                       = \\u05D4\\u05E7\\u05D8\\u05DF\nColumn.Deposit                        = \\u05D4\\u05E4\\u05E7\\u05D3\\u05D4\nColumn.Description                    = \\u05EA\\u05D0\\u05D5\\u05E8\nColumn.Due                            = Due\nColumn.Enabled                        = \\u05DE\\u05D5\\u05E4\\u05E2\\u05DC\nColumn.Entries                        = \\u05DB\\u05E0\\u05D9\\u05E1\\u05D5\\u05EA\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Exchange Rate\nColumn.Expense                        = \\u05D4\\u05D5\\u05E6\\u05D0\\u05D4\nColumn.Freq                           = \\u05EA\\u05D3\\u05D9\\u05E8\\u05D5\\u05EA\nColumn.Gain                           = Gain\nColumn.High                           = \\u05D2\\u05D1\\u05D5\\u05D4\nColumn.Income                         = \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4\nColumn.Increase                       = \\u05D4\\u05D2\\u05D3\\u05DC\nColumn.Investment                     = \\u05D4\\u05E9\\u05E7\\u05E2\\u05D4\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Loss\nColumn.Low                            = \\u05E0\\u05DE\\u05D5\\u05DA\nColumn.Memo                           = \\u05EA\\u05D6\\u05DB\\u05D9\\u05E8\nColumn.MktValue                       = Mkt Value\nColumn.Month                          = \\u05D7\\u05D5\\u05D3\\u05E9\nColumn.Num                            = .\\u05DE\\u05E1\nColumn.Payee                          = \\u05DE\\u05D5\\u05D8\\u05D1\nColumn.Payment                        = \\u05EA\\u05E9\\u05DC\\u05D5\\u05DD\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = \\u05DE\\u05D7\\u05D9\\u05E8\nColumn.Print                          = \\u05D4\\u05D3\\u05E4\\u05E1\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = \\u05DB\\u05DE\\u05D5\\u05EA\nColumn.Rebate                         = \\u05D4\\u05D7\\u05D6\\u05E8\nColumn.Receive                        = \\u05D4\\u05EA\\u05E7\\u05D1\\u05DC\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = \\u05D4\\u05D5\\u05E6\\u05D0\nColumn.Timestamp                      = \\u05D7\\u05D5\\u05EA\\u05DE\\u05EA \\u05D6\\u05DE\\u05DF\nColumn.Total                          = \\u05E1\\u05D4\"\\u05DB\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = \\u05E1\\u05D5\\u05D2\nColumn.Value                          = Value\nColumn.Volume                         = \\u05DB\\u05DE\\u05D5\\u05EA\nColumn.Withdrawal                     = \\u05DE\\u05E9\\u05D9\\u05DB\\u05D4\n\nDataStoreType.Bxds = \\u05E7\\u05D5\\u05D1\\u05E5 \\u05D1\\u05D9\\u05E0\\u05D0\\u05E8\\u05D9\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = \\u05E1\\u05DB\\u05D5\\u05DD\nItem.CashDeposit    = \\u05D4\\u05E4\\u05E7\\u05D3\\u05EA \\u05DE\\u05D6\\u05D5\\u05DE\\u05DF\nItem.CashWithdrawal = \\u05DE\\u05E9\\u05D9\\u05DB\\u05EA \\u05DE\\u05D6\\u05D5\\u05DE\\u05DF\nItem.Date           = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA\nItem.EFT            = EFT\nItem.Memo           = \\u05EA\\u05D6\\u05DB\\u05D9\\u05E8\nItem.NextNum        = \\u05D4\\u05D1\\u05D0#\nItem.Payee          = \\u05DE\\u05D5\\u05D8\\u05D1\nItem.Trans          = \\u05E4\\u05E2\\u05D5\\u05DC\\u05D4\n\nLabel.AccentColor         = Accent \\u05E6\\u05D1\\u05E2:\nLabel.Account             = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.AccountCode         = \\u05E7\\u05D5\\u05D3 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.AccountNumber       = \\u05DE\\u05E1\\u05E4\\u05E8 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.AccountOptions      = \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05D9\\u05D5\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.AccountSeparator    = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05DE\\u05E4\\u05E8\\u05D9\\u05D3:\nLabel.AccountStatus       = \\u05DE\\u05E6\\u05D1 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.AccountType         = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.Action              = Action:\nLabel.Amount              = \\u05E1\\u05DB\\u05D5\\u05DD:\nLabel.AnIntRate           = \\u05D0\\u05D7\\u05D5\\u05D6 \\u05E8\\u05D9\\u05D1\\u05D9\\u05EA \\u05E9\\u05E0\\u05EA\\u05D9 (APR):\nLabel.Available           = \\u05D6\\u05DE\\u05D9\\u05DF:\nLabel.Balance             = \\u05D9\\u05EA\\u05E8\\u05D4:\nLabel.BankAccount         = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05D1\\u05E0\\u05E7:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05D1\\u05E1\\u05D9\\u05E1:\nLabel.BaseColor           = \\u05E6\\u05D1\\u05E2 \\u05D1\\u05E1\\u05D9\\u05E1:\nLabel.Bottom              = Bottom:\nLabel.By                  = \\u05E2\"\\u05D9:\nLabel.CashBalance         = \\u05D9\\u05EA\\u05E8\\u05EA \\u05DE\\u05D6\\u05D5\\u05DE\\u05DF:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4:\nLabel.CompDaysPerYear     = Compounding Days per Year:\nLabel.CompPerTerm         = Compounding Periods per Year:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = \\u05DB\\u05DE\\u05D5\\u05EA:\nLabel.CreateCurr          = \\u05D9\\u05E6\\u05D9\\u05E8\\u05EA \\u05DE\\u05D8\\u05D1\\u05E2 \\u05DE\\u05D5\\u05EA\\u05D0\\u05DD:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = \\u05DE\\u05D8\\u05D1\\u05E2 / \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4:\nLabel.Currencies          = \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA:\nLabel.Currency            = Currency:\nLabel.Current             = \\u05E0\\u05D5\\u05DB\\u05D7\\u05D9:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA:\nLabel.DateFormat          = \\u05DE\\u05D1\\u05E0\\u05D4 \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA:\nLabel.DaysPastDue         = \\u05D9\\u05DE\\u05D9 \\u05D0\\u05D9\\u05D7\\u05D5\\u05E8:\nLabel.DefaultCurrency     = \\u05DE\\u05D8\\u05D1\\u05E2 \\u05D1\\u05E8\\u05D9\\u05E8\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC:\nLabel.DelaySec            = \\u05E2\\u05D9\\u05DB\\u05D5\\u05D1 (\\u05E9\\u05E0\\u05D9\\u05D5\\u05EA)\nLabel.Description         = \\u05EA\\u05D0\\u05D5\\u05E8:\nLabel.DestAccount         = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05DE\\u05E7\\u05D1\\u05DC:\nLabel.Difference          = \\u05D4\\u05E4\\u05E8\\u05E9:\nLabel.Dividend            = \\u05D3\\u05D9\\u05D1\\u05D9\\u05D3\\u05E0\\u05D3:\nLabel.EndDate             = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05E1\\u05D9\\u05D5\\u05DD:\nLabel.EndOn               = \\u05DE\\u05E1\\u05EA\\u05D9\\u05D9\\u05DD \\u05D1:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = \\u05D9\\u05EA\\u05E8\\u05EA \\u05E1\\u05D9\\u05D5\\u05DD:\nLabel.EquityAccount       = Equity \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = \\u05DB\\u05DC:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = \\u05E9\\u05E2\\u05E8 \\u05D7\\u05DC\\u05D9\\u05E4\\u05D9\\u05DF:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = \\u05E2\\u05DE\\u05DC\\u05D4:\nLabel.FeesAccount         = \\u05E2\\u05DE\\u05DC\\u05D5\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.FileName            = \\u05E9\\u05DD \\u05E7\\u05D5\\u05D1\\u05E5:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05EA\\u05E9\\u05DC\\u05D5\\u05DD \\u05E8\\u05D0\\u05E9\\u05D5\\u05DF:\nLabel.FocusColor          = \\u05D3\\u05D2\\u05E9 \\u05E6\\u05D1\\u05E2:\nLabel.Format              = Format:\nLabel.Frequency           = \\u05EA\\u05D3\\u05D9\\u05E8\\u05D5\\u05EA:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gains:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = \\u05D2\\u05D5\\u05D1\\u05D4\nLabel.High                = \\u05D2\\u05D1\\u05D5\\u05D4:\nLabel.Host                = \\u05E9\\u05E8\\u05EA:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Income Account:\nLabel.InterestAccount     = \\u05E8\\u05D9\\u05D1\\u05D9\\u05D5\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF:\nLabel.LastOccurrence      = Last Occurrence:\nLabel.Layout              = Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = \\u05EA\\u05E7\\u05D5\\u05E4\\u05EA \\u05D4\\u05D4\\u05DC\\u05D5\\u05D5\\u05D0\\u05D4 (\\u05D7\\u05D5\\u05D3\\u05E9\\u05D9\\u05DD):\nLabel.Low                 = \\u05E0\\u05DE\\u05D5\\u05DA:\nLabel.MarketValue         = \\u05E2\\u05E8\\u05DA \\u05E9\\u05D5\\u05E7:\nLabel.MaxBackupCount      = Maximum number of backup files to keep:\nLabel.Memo                = \\u05EA\\u05D6\\u05DB\\u05D9\\u05E8:\nLabel.Monospace           = \\u05D2\\u05D5\\u05D3\\u05DC \\u05E7\\u05D1\\u05D5\\u05E2:\nLabel.Name                = \\u05E9\\u05DD:\nLabel.NetIncome           = \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4 \\u05E0\\u05E7\\u05D9\\u05D9\\u05D4:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05D4\\u05D1\\u05D0 \\u05DC\\u05EA\\u05E9\\u05DC\\u05D5\\u05DD:\nLabel.Notes               = \\u05D4\\u05E2\\u05E8\\u05D5\\u05EA:\nLabel.NumTrans            = Number of Transactions:\nLabel.Number              = \\u05DE\\u05E1\\u05E4\\u05E8:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date:\nLabel.OpeningBalance      = \\u05D9\\u05EA\\u05E8\\u05EA \\u05E4\\u05EA\\u05D9\\u05D7\\u05D4:\nLabel.OrigLoanAmt         = \\u05E1\\u05DB\\u05D5\\u05DD \\u05D4\\u05DC\\u05D5\\u05D5\\u05D0\\u05D4 \\u05DE\\u05E7\\u05D5\\u05E8\\u05D9:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = \\u05E1\\u05D9\\u05E1\\u05DE\\u05D4:\nLabel.Path                = Path\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = \\u05EA\\u05E9\\u05DC\\u05D5\\u05DE\\u05D9\\u05DD \\u05DC\\u05E9\\u05E0\\u05D4:\nLabel.Payee               = \\u05DE\\u05D5\\u05D8\\u05D1:\nLabel.Period              = Period\nLabel.Port                = Port:\nLabel.Prefix              = \\u05E7\\u05D9\\u05D3\\u05D5\\u05DE\\u05EA:\nLabel.Price               = \\u05DE\\u05D7\\u05D9\\u05E8:\nLabel.Proportional        = \\u05D2\\u05D5\\u05D3\\u05DC \\u05D9\\u05D7\\u05E1\\u05D9:\nLabel.Quantity            = \\u05DB\\u05DE\\u05D5\\u05EA:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = \\u05D9\\u05EA\\u05E8\\u05D4 \\u05DE\\u05EA\\u05D5\\u05D0\\u05DE\\u05EA:\nLabel.RemindLater         = \\u05D4\\u05D6\\u05DB\\u05E8 \\u05DC\\u05D9 \\u05E9\\u05D5\\u05D1 \\u05D0\\u05D7\\u05E8\\u05D9:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = \\u05D7\\u05D6\\u05D5\\u05E8 \\u05D1:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = \\u05E8\\u05D6\\u05D5\\u05DC\\u05D5\\u05E6\\u05D9\\u05D4:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = \\u05E1\\u05D5\\u05BC\\u05DC\\u05B8\\u05DD:\nLabel.Securities          = \\u05DE\\u05E0\\u05D9\\u05D5\\u05EA:\nLabel.Security            = \\u05DE\\u05E0\\u05D9\\u05D4:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = \\u05D4\\u05E8\\u05D0\\u05D4 \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05E8\\u05D9\\u05E7\\u05D9\\u05DD\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05D4\\u05EA\\u05D7\\u05DC\\u05D4:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Start Position:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA \\u05D4\\u05EA\\u05D7\\u05DC\\u05D4\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = \\u05E1\\u05D9\\u05D5\\u05DE\\u05EA:\nLabel.Symbol              = \\u05E1\\u05D9\\u05DE\\u05D5\\u05DF:\nLabel.TargetBalance       = Target Balance:\nLabel.Top                 = Top:\nLabel.Total               = \\u05E1\\u05D4\"\\u05DB:\nLabel.Transaction         = Transaction:\nLabel.TransferFrom        = \\u05D4\\u05D5\\u05E2\\u05D1\\u05E8 \\u05DE:\nLabel.TransferTo          = \\u05D4\\u05D5\\u05E2\\u05D1\\u05E8 \\u05DC:\nLabel.Type                = \\u05E1\\u05D5\\u05D2:\nLabel.Units               = Units:\nLabel.UserName            = \\u05E9\\u05DD \\u05DE\\u05E9\\u05EA\\u05DE\\u05E9:\nLabel.Value               = Value:\nLabel.Verify              = \\u05D0\\u05DE\\u05EA:\nLabel.VerifyPassword      = Verify Password:\nLabel.Visible             = \\u05E0\\u05E8\\u05D0\\u05D4:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = \\u05D0\\u05D5\\u05D3\\u05D5\\u05EA\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nMenu.AccountRegister.Name             = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF Register...\nMenu.AddRemoveCurrency.Name=\\u05D4\\u05D5\\u05E1\\u05E4\\u05D4\\u05D4\\u05E1\\u05E8\\u05D4\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA \\u05E2\\u05D3\\u05DB\\u05D5\\u05DF\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = \\u05E0\\u05D9\\u05D9\\u05E8\\u05D5\\u05EA \\u05E2\\u05E8\\u05DA Update\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = v\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = \\u05E0\\u05D5\\u05E7\\u05D4\nMenu.Close.Name                       = \\u05E1\\u05D2\\u05D9\\u05E8\\u05D4\nMenu.Close.Tooltip                    = \\u05E1\\u05D2\\u05D5\\u05E8 \\u05E7\\u05D5\\u05D1\\u05E5 \\u05E4\\u05E2\\u05D9\\u05DC\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = \\u05D4\\u05E2\\u05EA\\u05E7\\u05D4\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Change _default...\nMenu.DefaultCurrency.Tooltip          = \\u05E9\\u05E0\\u05D4 \\u05DE\\u05D8\\u05D1\\u05E2 \\u05D1\\u05E8\\u05D9\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC\nMenu.Delete.Name                      = \\u05DE\\u05D7\\u05D9\\u05E7\\u05D4\nMenu.Duplicate.Name                   = \\u05E9\\u05D9\\u05DB\\u05E4\\u05D5\\u05DC\nMenu.Edit.Name                        = \\u05E2\\u05E8\\u05D9\\u05DB\\u05D4\nMenu.EditTranNumList.Name             = Edit transaction number list\nMenu.Exit.Name                        = \\u05E6\\u05D0\nMenu.Exit.Tooltip                     = jGnash \\u05D9\\u05E6\\u05D9\\u05D0\\u05D4 \\u05DE\nMenu.Export.Name                      = \\u05D9\\u05E6\\u05D5\\u05D0\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = \\u05D0\\u05E8\\u05DB\\u05D9\\u05D5\\u05DF...\nMenu.File.Name                        = \\u05E7\\u05D5\\u05D1\\u05E5\nMenu.Filter.Name                      = \\u05DE\\u05E1\\u05E0\\u05E0\\u05D9\\u05DD\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = \\u05E2\\u05D6\\u05E8\\u05D4\nMenu.Hide.Name                        = \\u05D4\\u05E1\\u05EA\\u05E8\nMenu.HistoryChart.Name                = \\u05EA\\u05E8\\u05E9\\u05D9\\u05DD \\u05D4\\u05D9\\u05E1\\u05D8\\u05D5\\u05E8\\u05D9...\nMenu.HistoryCommodity.Name            = \\u05E1\\u05D7\\u05D5\\u05E8\\u05D5\\u05EA...\nMenu.HistoryImport.Name               = \\u05D9\\u05D1\\u05D5\\u05D0 \\u05D4\\u05D9\\u05E1\\u05D8\\u05D5\\u05E8\\u05D9...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = \\u05EA\\u05E8\\u05E9\\u05D9\\u05DD \\u05D4\\u05DB\\u05E0\\u05E1\\u05D5\\u05EA \\u05D4\\u05D5\\u05E6\\u05D0\\u05D5\\u05EA...\nMenu.Import.Name                      = \\u05D9\\u05D1\\u05D5\\u05D0\nMenu.ImportAccounts.Name              = Import Accounts...\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Import jGnash...\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF...\nMenu.Jump.Name                        = \\u05E2\\u05D1\\u05D5\\u05E8\nMenu.ListOfAccounts.Name              = \\u05E8\\u05E9\\u05D9\\u05DE\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA\\u2026\nMenu.Locale.Name                      = \\u05E9\\u05E0\\u05D4 \\u05D4\\u05D2\\u05D3\\u05E8\\u05D5\\u05EA \\u05DE\\u05D9\\u05E7\\u05D5\\u05DD...\nMenu.LookAndFeel.Name                 = \\u05DE\\u05E8\\u05D0\\u05D4\nMenu.MarkAs.Name                      = \\u05E1\\u05DE\\u05DF \\u05DB\nMenu.Modify.Name                      = \\u05E9\\u05E0\\u05D4\nMenu.ModifyCommodity.Name             = \\u05D9\\u05E6\\u05D9\\u05E8\\u05D4 \\u05D5\\u05E9\\u05D9\\u05E0\\u05D5\\u05D9\nMenu.ModifyCurrency.Name              = \\u05E9\\u05E0\\u05D4...\nMenu.ModifyExchangeRates.Name         = Edit Exchange Rates...\nMenu.MonthBalance.Name                = \\u05DE\\u05D0\\u05D6\\u05DF \\u05D7\\u05D5\\u05D3\\u05E9\\u05D9...\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = \\u05DE\\u05D0\\u05D6\\u05DF \\u05E1\\u05D5\\u05E3-\\u05D7\\u05D5\\u05D3\\u05E9\nMenu.MonthEndBalanceCSV.Name          = \\u05DE\\u05D0\\u05D6\\u05DF \\u05E1\\u05D5\\u05E3-\\u05D7\\u05D5\\u05D3\\u05E9 (CSV)\nMenu.NetWorth.Name                    = \\u05E9\\u05D5\\u05D5\\u05D9 \\u05E0\\u05E7\\u05D9...\nMenu.New.Name                         = \\u05D7\\u05D3\\u05E9\nMenu.New.Tooltip                      = \\u05D9\\u05E6\\u05D9\\u05E8\\u05EA \\u05E7\\u05D5\\u05D1\\u05E5 \\u05D7\\u05D3\\u05E9\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = \\u05E4\\u05EA\\u05D9\\u05D7\\u05D4\nMenu.Open.Tooltip                     = \\u05E4\\u05EA\\u05D9\\u05D7\\u05D4 \\u05E9\\u05DC \\u05D4\\u05E7\\u05D5\\u05D1\\u05E5 \\u05D4\\u05E0\\u05D1\\u05D7\\u05E8\nMenu.Option.Name                      = \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05D9\\u05D5\\u05EA...\nMenu.Option.Tooltip                   = \\u05E9\\u05E0\\u05D4 \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05D9\\u05D5\\u05EA\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = \\u05D9\\u05EA\\u05E8\\u05D4 \\u05D1\\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05EA\\u05E7\\u05D5\\u05E4\\u05EA\\u05D9\\u05EA\nMenu.Portfolio.Name                   = Portfolio...\nMenu.ProfitLoss.Name                  = \\u05E8\\u05D5\\u05D5\\u05D7 \\u05D5\\u05D4\\u05E4\\u05E1\\u05D3...\nMenu.ProfitLossTXT.Name               = \\u05E8\\u05D5\\u05D5\\u05D7 \\u05D5\\u05D4\\u05E4\\u05E1\\u05D3 (\\u05D8\\u05E7\\u05E1\\u05D8)\nMenu.Reconcile.Name                   = \\u05EA\\u05D0\\u05D5\\u05DD\nMenu.Reconciled.Name                  = \\u05DE\\u05EA\\u05D5\\u05D0\\u05DD\nMenu.RecurringList.Name               = \\u05E4\\u05E2\\u05D5\\u05DC\\u05D5\\u05EA \\u05D7\\u05D5\\u05D6\\u05E8\\u05D5\\u05EA...\nMenu.Register.Name                    = \\u05E4\\u05E8\\u05D8\\u05D9 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF..\nMenu.Reports.Name                     = \\u05D3\\u05D5\\u05D7\\u05D5\\u05EA\nMenu.RunJavaScript.Name               = Run JavaScript...\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = \\u05E9\\u05DE\\u05D9\\u05E8\\u05D4\nMenu.Save.Tooltip                     = \\u05E9\\u05DE\\u05D9\\u05E8\\u05EA \\u05E7\\u05D5\\u05D1\\u05E5 \\u05E0\\u05D5\\u05DB\\u05D7\\u05D9\nMenu.SaveAs.Name                      = ...\\u05E9\\u05DE\\u05D5\\u05E8 \\u05D1\\u05E9\\u05DD\nMenu.SaveAs.Tooltip                   = \\u05E9\\u05DE\\u05D5\\u05E8 \\u05E9\\u05D5\\u05D1\\u05E5 \\u05E0\\u05D5\\u05DB\\u05D7\\u05D9 \\u05D1\\u05E9\\u05DD \\u05D7\\u05D3\\u05E9\nMenu.Securities.Name                  = \\u05E1\\u05D7\\u05D5\\u05E8\\u05D5\\u05EA\nMenu.SetPassword.Name                 = \\u05E7\\u05D1\\u05E2 \\u05E1\\u05D9\\u05E1\\u05DE\\u05D4...\nMenu.Show.Name                        = \\u05D4\\u05E6\\u05D2\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = \\u05E2\\u05E8\\u05DB\\u05D5\\u05EA \\u05E0\\u05D5\\u05E9\\u05D0\nMenu.Tools.Name                       = \\u05DB\\u05DC\\u05D9\\u05DD\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = \\u05DC\\u05D0 \\u05DE\\u05EA\\u05D5\\u05D0\\u05DD\nMenu.View.Name                        = \\u05EA\\u05E6\\u05D5\\u05D2\\u05D4\nMenu.Window.Name                      = Window\n\nMessage.AcceptLicense                = I have read, understand, and accept the terms of the licenses.\nMessage.AccountAdd                   = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05D4\\u05D5\\u05E1\\u05E3\nMessage.AccountCode                  = \\u05E7\\u05D5\\u05D3 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05E7\\u05D9\\u05D9\\u05DD: \\u05E7\\u05D5\\u05D3 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05DC\\u05D0 \\u05E9\\u05D5\\u05E0\\u05D4\nMessage.AccountLocked                = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05E0\\u05E2\\u05D5\\u05DC\nMessage.AccountModify                = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05E9\\u05D5\\u05E0\\u05D4\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05D4\\u05D5\\u05E1\\u05E8\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = \\u05E9\\u05DE\\u05D9\\u05E8\\u05D4 \\u05D0\\u05D5\\u05D8\\u05D5\\u05DE\\u05D8\\u05D9\\u05EA \\u05DE\\u05D5\\u05E4\\u05E1\\u05E7\\u05EA\nMessage.AutoSaveOn                   = \\u05E9\\u05DE\\u05D9\\u05E8\\u05D4 \\u05D0\\u05D5\\u05D8\\u05D5\\u05DE\\u05D8\\u05D9\\u05EA \\u05DE\\u05D5\\u05E4\\u05E2\\u05DC\\u05EA\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = \\u05D7\\u05D9\\u05E4\\u05D5\\u05E9 \\u05D0\\u05D9\\u05E8\\u05D5\\u05E2\\u05D9\\u05DD \\u05D7\\u05D5\\u05D6\\u05E8\\u05D9\\u05DD\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Delete the selected transactions?\nMessage.ConfirmReminderDelete        = \\u05D4\\u05D0\\u05D9\\u05DD \\u05DC\\u05D1\\u05D7\\u05D5\\u05E8 \\u05D0\\u05EA \\u05D4\\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05EA \\u05E9\\u05E0\\u05D1\\u05D7\\u05E8\\u05D4?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Delete the selected transaction?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = \\u05DE\\u05D8\\u05D1\\u05E2 \\u05D1\\u05E8\\u05D9\\u05E8\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC \\u05E9\\u05D5\\u05E0\\u05D4 \\u05DC:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = \\u05DE\\u05E0\\u05D5\\u05E2 \\u05D0\\u05D5\\u05EA\\u05D7\\u05DC\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Unable to create \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nMessage.Error.AccountRemove          = Could not remove \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = \\u05D0\\u05D9\\u05DF \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05EA \\u05DC\\u05D4\\u05D5\\u05E1\\u05D9\\u05E3 \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4\nMessage.Error.AddCurrency            = \\u05D0\\u05D9\\u05DF \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05EA \\u05DC\\u05D4\\u05D5\\u05E1\\u05D9\\u05E3 \\u05DE\\u05D8\\u05D1\\u05E2\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Create a basic \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = \\u05E7\\u05D5\\u05D1\\u05E5 \\u05DC\\u05D0 \\u05E0\\u05DE\\u05E6\\u05D0\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = \\u05E8\\u05D9\\u05E9\\u05D9\\u05D5\\u05DF \\u05E9\\u05D9\\u05DE\\u05D5\\u05E9 \\u05DC\\u05D0 \\u05D4\\u05EA\\u05E7\\u05D1\\u05DC\nMessage.Error.LoadingFile            = \\u05E9\\u05D2\\u05D9\\u05D0\\u05D4 \\u05D1\\u05D8\\u05E2\\u05D9\\u05E0\\u05EA \\u05D4\\u05E7\\u05D5\\u05D1\\u05E5\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = \\u05D0\\u05D9\\u05DF \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05EA \\u05DC\\u05E9\\u05E0\\u05D5\\u05EA \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4\nMessage.Error.ModifyCurrency         = \\u05D0\\u05D9\\u05DF \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05EA \\u05DC\\u05E9\\u05E0\\u05D5\\u05EA \\u05DE\\u05D8\\u05D1\\u05E2\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = \\u05E7\\u05D5\\u05D1\\u05E5 \\u05E1\\u05D2\\u05D5\\u05E8\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Default locale changed to:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = \\u05DE\\u05EA\\u05DB\\u05D5\\u05E0\\u05DF \\u05DC\\u05D9\\u05E6\\u05D9\\u05D0\\u05D4\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Report modules were loaded successfully\nMessage.ReportWait                   = Please wait, Loading report modules\nMessage.RestartLocale                = \\u05D4\\u05E4\\u05E2\\u05DC \\u05DE\\u05D7\\u05D3\\u05E9 \\u05E2\"\\u05DE \\u05DC\\u05D4\\u05E4\\u05E2\\u05D9\\u05DC \\u05E9\\u05D9\\u05E0\\u05D5\\u05D9\\u05D9\\u05DD\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = \\u05D4\\u05DB\\u05E0\\u05E1 \\u05EA\\u05D0\\u05E8\\u05D9\\u05DB\\u05D9 \\u05D4\\u05EA\\u05D7\\u05DC\\u05D4 \\u05D5\\u05E1\\u05D9\\u05D5\\u05DD\nMessage.StoreBackup                  = \\u05E9\\u05DE\\u05D9\\u05E8\\u05EA \\u05D2\\u05D9\\u05D1\\u05D5\\u05D9 \\u05DC\nMessage.StoreComplete                = \\u05E9\\u05DE\\u05D9\\u05E8\\u05EA \\u05E7\\u05D5\\u05D1\\u05E5 \\u05D4\\u05D5\\u05E9\\u05DC\\u05DE\\u05D4\nMessage.StoreWait                    = \\u05DE\\u05DE\\u05EA\\u05D9\\u05DF \\u05DC\\u05E1\\u05D9\\u05D5\\u05DD \\u05E9\\u05DE\\u05D9\\u05E8\\u05EA \\u05D4\\u05E7\\u05D5\\u05D1\\u05E5\nMessage.TXTFile                      = Text Files (*.txt)\nMessage.TransactionAccountLocked     = Transaction add failed, Destination account(s) are locked\nMessage.TransactionAdd               = Transaction added\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transaction removed\nMessage.TransactionRemoveLocked      = Transaction removal failed, Destination account(s) are locked\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4 \\u05D1\\u05E9\\u05D9\\u05DE\\u05D5\\u05E9\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = \\u05DE\\u05D8\\u05D1\\u05E2 \\u05D1\\u05E9\\u05D9\\u05DE\\u05D5\\u05E9\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D1\\u05E0\\u05E7\nName.ExpenseAccounts = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D4\\u05D5\\u05E6\\u05D0\\u05D4\nName.IncomeAccounts  = \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = From {0,date,long} To {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = \\u05E2\\u05DE\\u05D5\\u05D3 {0} \\u05DE {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = \\u05D3\\u05E7\\u05D5\\u05EA 10\nPeriod.15Min     = 15 \\u05D3\\u05E7\\u05D5\\u05EA\nPeriod.1Day      = 1 \\u05D9\\u05D5\\u05DD\nPeriod.1Hr       = 1 \\u05E9\\u05E2\\u05D4\nPeriod.2Hr       = 2 \\u05E9\\u05E2\\u05D5\\u05EA\nPeriod.30Min     = 30 \\u05E9\\u05E2\\u05D5\\u05EA\nPeriod.5Min      = 5 \\u05D3\\u05E7\\u05D5\\u05EA\nPeriod.8Hr       = 8 \\u05E9\\u05E2\\u05D5\\u05EA\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = \\u05D9\\u05D5\\u05DE\\u05D9\nPeriod.Monthly   = \\u05D7\\u05D5\\u05D3\\u05E9\\u05D9\nPeriod.NextStart = \\u05D1\\u05E4\\u05E2\\u05DD \\u05D4\\u05D1\\u05D0\\u05D4 \\u05E9jGnash \\u05DE\\u05D0\\u05D5\\u05EA\\u05D7\\u05DC\nPeriod.None      = None\nPeriod.OnlyOnce  = \\u05D7\\u05D3 \\u05E4\\u05E2\\u05DE\\u05D9\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = \\u05E9\\u05D1\\u05D5\\u05E2\\u05D9\nPeriod.Yearly    = \\u05E9\\u05E0\\u05EA\\u05D9\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = \\u05D0\\u05D5\\u05D3\\u05D5\\u05EA\nTab.Accounts        = Accounts\nTab.Adjust          = \\u05EA\\u05D0\\u05D5\\u05DD\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Charge\nTab.Credit          = \\u05D6\\u05DB\\u05D5\\u05EA\nTab.Credits         = Credits\nTab.DataEngine      = \\u05DE\\u05E0\\u05D5\\u05E2 \\u05DE\\u05D9\\u05D3\\u05E2\nTab.DataProviders   = Data Providers\nTab.Day             = \\u05D9\\u05D5\\u05DD\nTab.Debit           = \\u05D7\\u05D5\\u05D1\\u05D4\nTab.Formats         = Formats\nTab.GPLLicense      = GPL \\u05E8\\u05D9\\u05E9\\u05D9\\u05D5\\u05DF\nTab.General         = \\u05DB\\u05DC\\u05DC\\u05D9\nTab.LGPLLicense     = LGPL \\u05E8\\u05D9\\u05E9\\u05D9\\u05D5\\u05DF\nTab.License         = \\u05E8\\u05D9\\u05E9\\u05D9\\u05D5\\u05DF\nTab.Month           = \\u05D7\\u05D5\\u05D3\\u05E9\nTab.Network         = Network\nTab.None            = None\nTab.Payment         = \\u05EA\\u05E9\\u05DC\\u05D5\\u05DD\nTab.Register        = Register\nTab.Reminders       = \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05D5\\u05EA\nTab.Report          = \\u05D3\\u05D5\"\\u05D7\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = \\u05DE\\u05D9\\u05D3\\u05E2 \\u05DE\\u05E2\\u05E8\\u05DB\\u05EA\nTab.Transfer        = \\u05D4\\u05E2\\u05D1\\u05E8\\u05D4\nTab.Week            = \\u05E9\\u05D1\\u05D5\\u05E2\nTab.Year            = \\u05E9\\u05E0\\u05D4\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = \\u05D0\\u05D5\\u05D3\\u05D5\\u05EA\nTitle.AccountBalance             = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05DE\\u05D0\\u05D6\\u05DF\nTitle.AccountFilter              = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05DE\\u05E1\\u05E0\\u05E0\\u05D9\\u05DD\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF Securities\nTitle.AddRemCurr                 = \\u05D4\\u05D5\\u05E1\\u05E4\\u05D4 \\u05D5\\u05D4\\u05E1\\u05E8\\u05D4 \\u05E9\\u05DC \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA\nTitle.AmortizationSetup          = \\u05D4\\u05D2\\u05D3\\u05E8\\u05D5\\u05EA \\u05E4\\u05D7\\u05EA\nTitle.Archive                    = \\u05D0\\u05E8\\u05DB\\u05D9\\u05D1\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = \\u05E9\\u05DE\\u05D9\\u05E8\\u05D4 \\u05D0\\u05D5\\u05D8\\u05D5\\u05DE\\u05D8\\u05D9\\u05EA\nTitle.Available                  = Available\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Backing Store\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = \\u05DE\\u05E2\\u05E6\\u05D1 \\u05D4\\u05DE\\u05D7\\u05D0\\u05D5\\u05EA\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Column Visibility\nTitle.Colors                     = \\u05E6\\u05D1\\u05E2\\u05D9\\u05DD\nTitle.CommoditiesSecurities      = Commodities / Securities\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = \\u05D0\\u05E9\\u05E8\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Create / Modify Commodities\nTitle.Credits                    = Credits\nTitle.Currencies                 = \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA\nTitle.Current                    = \\u05E0\\u05D5\\u05DB\\u05D7\\u05D9\nTitle.CutOffDate                 = Select Cut Off Date\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Debits\nTitle.DefDefCurr                 = Define Default Currency\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = \\u05D1\\u05E8\\u05D9\\u05E8\\u05D5\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Display\nTitle.DuplicateTransaction       = Duplicate Transaction\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = End-Of-Month \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF Balance\nTitle.EnterPassword              = \\u05D4\\u05D6\\u05DF \\u05D0\\u05EA \\u05D4\\u05E1\\u05D9\\u05E1\\u05DE\\u05D4\nTitle.Entry                      = Entry\nTitle.Error                      = Error\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Exchange Rate\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = \\u05DE\\u05E1\\u05E0\\u05DF\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Frequency\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historical Import\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Import a partial QIF file\nTitle.ImpSum                     = Import Summary\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = \\u05DE\\u05D9\\u05D3\\u05E2\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Modify QIF Transactions\nTitle.ModifyAccount              = \\u05E9\\u05E0\\u05D4 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nTitle.ModifyCurrencies           = \\u05E9\\u05E0\\u05D4 \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA\nTitle.ModifyReminder             = \\u05E9\\u05E0\\u05D4 \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05EA\nTitle.ModifySecHistory           = Modify Securities History\nTitle.ModifyTransaction          = Modify Transaction\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05D7\\u05D3\\u05E9\nTitle.NewBudget                  = \\u05EA\\u05E7\\u05E6\\u05D9\\u05D1 \\u05D7\\u05D3\\u05E9\nTitle.NewFile                    = \\u05E6\\u05D5\\u05E8 \\u05E7\\u05D5\\u05D1\\u05E5 \\u05D7\\u05D3\\u05E9\nTitle.NewPassword                = New Password\nTitle.NewReminder                = \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05EA \\u05D7\\u05D3\\u05E9\\u05D4\nTitle.NewTrans                   = New Transaction\nTitle.Notes                      = \\u05D4\\u05E2\\u05E8\\u05D5\\u05EA\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Open\nTitle.Options                    = \\u05D0\\u05E4\\u05E9\\u05E8\\u05D5\\u05D9\\u05D5\\u05EA\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = \\u05D4\\u05D2\\u05D3\\u05E8\\u05EA \\u05E2\\u05DE\\u05D5\\u05D3\nTitle.ParentAccount              = \\u05D0\\u05D1 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nTitle.PercentDist                = \\u05D4\\u05EA\\u05E4\\u05DC\\u05D2\\u05D5\\u05EA \\u05D0\\u05D7\\u05D5\\u05D6\\u05D9\\u05DD\nTitle.PercentExpense             = \\u05D0\\u05D7\\u05D5\\u05D6 \\u05D4\\u05D5\\u05E6\\u05D0\\u05D5\\u05EA\nTitle.PercentIncome              = \\u05D0\\u05D7\\u05D5\\u05D6 \\u05D4\\u05DB\\u05E0\\u05E1\\u05D5\\u05EA\nTitle.PleaseWait                 = \\u05D0\\u05E0\\u05D0 \\u05D4\\u05DE\\u05EA\\u05DF\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profit and Loss Statement\nTitle.ReconcileSettings          = \\u05D4\\u05D2\\u05D3\\u05E8\\u05D5\\u05EA \\u05EA\\u05D0\\u05D5\\u05DD\nTitle.Reminder                   = \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05EA\nTitle.Reminders                  = \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05D5\\u05EA\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Save As\nTitle.SaveFile                   = \\u05E9\\u05DE\\u05D5\\u05E8 \\u05E7\\u05D5\\u05D1\\u05E5\nTitle.SecurityHistory            = Security History\nTitle.SelAccount                 = \\u05D1\\u05D7\\u05E8 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nTitle.SelAvailCurr               = \\u05D1\\u05D7\\u05E8 \\u05DE\\u05D8\\u05D1\\u05E2\\u05D5\\u05EA \\u05D0\\u05E4\\u05E9\\u05E8\\u05D9\\u05D9\\u05DD\nTitle.SelDate                    = \\u05D1\\u05D7\\u05E8 \\u05EA\\u05D0\\u05E8\\u05D9\\u05DA\nTitle.SelDefCurr                 = \\u05D1\\u05D7\\u05E8 \\u05DE\\u05D8\\u05D1\\u05E2 \\u05D1\\u05E8\\u05D9\\u05E8\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC\nTitle.SelDefLocale               = \\u05D1\\u05D7\\u05E8 \\u05DE\\u05D9\\u05E7\\u05D5\\u05DD \\u05D1\\u05E8\\u05D9\\u05E8\\u05EA \\u05DE\\u05D7\\u05D3\\u05DC\nTitle.SelDestAccount             = \\u05D1\\u05D7\\u05E8 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05DE\\u05D8\\u05E8\\u05D4\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Select Equity \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nTitle.SelFile                    = \\u05D1\\u05D7\\u05E8 \\u05E7\\u05D5\\u05D1\\u05E5\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = \\u05D1\\u05D7\\u05E8 \\u05E6\\u05D1\\u05E2\nTitle.Selected                   = \\u05E0\\u05D1\\u05D7\\u05E8\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaction\nTitle.Startup                    = Startup\nTitle.Steps                      = Steps\nTitle.Success                    = \\u05D4\\u05B7\\u05E6\\u05DC\\u05B8\\u05D7\\u05B8\\u05D4\nTitle.Summary                    = \\u05E1\\u05D9\\u05DB\\u05D5\\u05DD\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaction\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Transaction List\nTitle.TransactionSetup           = Transaction Setup\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Visible\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = \\u05E8\\u05E9\\u05D9\\u05DE\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA\nToolTip.AccountRegister                      = \\u05E4\\u05E8\\u05D8\\u05D9 \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = \\u05E9\\u05E0\\u05D4 \\u05E0\\u05E8\\u05D0\\u05D5\\u05EA \\u05E9\\u05DC \\u05E2\\u05DE\\u05D5\\u05D3\\u05D5\\u05EA\nToolTip.ConvertSEntry                        = Change this transaction into a Double Entry transaction\nToolTip.DeleteAccount                        = \\u05DE\\u05D7\\u05E7 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = \\u05E1\\u05E0\\u05DF \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA\nToolTip.FilterAccounts                       = \\u05E1\\u05E0\\u05DF \\u05E2\"\\u05E4 \\u05E1\\u05D5\\u05D2 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = \\u05E1\\u05E0\\u05DF \\u05DC\\u05E4\\u05D9 \\u05D4\\u05EA\\u05E4\\u05D9\\u05D9\\u05E1\\u05D5 \\u05D4\\u05DE\\u05D3\\u05D9\\u05E0\\u05D4\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = \\u05DE\\u05E1\\u05E4\\u05E8\\u05D9\\u05DD \\u05E9\\u05DC\\u05DE\\u05D9\\u05DD \\u05D1\\u05DC\\u05D1\\u05D3 \\u05DE\\u05D5\\u05EA\\u05E8\nToolTip.ModifyAccount                        = \\u05E9\\u05E0\\u05D4 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nToolTip.NewAccount                           = \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF \\u05D7\\u05D3\\u05E9\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = \\u05D4\\u05D3\\u05E4\\u05E1 \\u05E4\\u05E8\\u05D8\\u05D9 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nToolTip.ReconcileAccount                     = \\u05EA\\u05D9\\u05D0\\u05D5\\u05DD \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nToolTip.Reminders                            = \\u05EA\\u05D6\\u05DB\\u05D5\\u05E8\\u05D5\\u05EA\nToolTip.ResizeColumns                        = \\u05E9\\u05E0\\u05D4 \\u05D2\\u05D5\\u05D3\\u05DC \\u05E2\\u05DE\\u05D5\\u05D3\\u05D5\\u05EA\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = \\u05DE\\u05E1\\u05E4\\u05E8 \\u05D4\\u05E1\\u05E4\\u05E8\\u05D5\\u05EA \\u05DE\\u05D4\\u05D9\\u05DE\\u05D9\\u05DF \\u05DC\\u05E0\\u05E7\\u05D5\\u05D3\\u05D4 \\u05D4\\u05E2\\u05E9\\u05E8\\u05D5\\u05E0\\u05D9\\u05EA\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = \\u05E6\\u05D5\\u05E8 \\u05D2\\u05D9\\u05D1\\u05D5\\u05D9 \\u05E2\\u05DD \\u05D7\\u05EA\\u05D9\\u05DE\\u05EA \\u05D6\\u05DE\\u05DF \\u05D1\\u05E1\\u05D2\\u05D9\\u05E8\\u05D4\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = \\u05E4\\u05EA\\u05D7 \\u05E4\\u05E8\\u05D8\\u05D9 \\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\n\nTransaction.AddShare        = \\u05D4\\u05D5\\u05E1\\u05E4\\u05EA \\u05DE\\u05E0\\u05D9\\u05D5\\u05EA\nTransaction.BuyShare        = \\u05E7\\u05E0\\u05D9\\u05EA \\u05DE\\u05E0\\u05D9\\u05D5\\u05EA\nTransaction.Dividend        = \\u05D3\\u05D9\\u05D1\\u05D9\\u05D3\\u05E0\\u05D3\nTransaction.DoubleEntry     = \\u05D4\\u05E0\\u05D4\\u05DC\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D3\\u05D5 \\u05E6\\u05D9\\u05D3\\u05D9\\u05EA\nTransaction.MergeShare      = \\u05D0\\u05D9\\u05D7\\u05D5\\u05D3 \\u05DE\\u05E0\\u05D9\\u05D5\\u05EA\nTransaction.ReinvestDiv     = \\u05D4\\u05E9\\u05E7\\u05E2\\u05EA \\u05D3\\u05D9\\u05D1\\u05D9\\u05D3\\u05E0\\u05D3\nTransaction.RemoveShare     = \\u05D4\\u05E1\\u05E8\\u05EA \\u05DE\\u05E0\\u05D9\\u05D5\\u05EA\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = \\u05DE\\u05DB\\u05D9\\u05E8\\u05EA \\u05DE\\u05E0\\u05D9\\u05D5\\u05EA\nTransaction.SingleEntry     = \\u05D4\\u05E0\\u05D4\\u05DC\\u05EA \\u05D7\\u05E9\\u05D1\\u05D5\\u05E0\\u05D5\\u05EA \\u05D7\\u05D3 \\u05E6\\u05D9\\u05D3\\u05D9\\u05EA\nTransaction.Split           = \\u05E4\\u05D9\\u05E6\\u05D5\\u05DC \\u05E4\\u05E2\\u05D5\\u05DC\\u05D4\nTransaction.SplitEntry      = Split Transaction Entry\nTransaction.SplitShare      = \\u05E4\\u05D9\\u05E6\\u05D5\\u05DC \\u05DE\\u05E0\\u05D9\\u05D4\nTransaction.TransferIn      = \\u05D4\\u05E2\\u05D1\\u05E8\\u05D4 \\u05DC\\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\nTransaction.TransferOut     = \\u05D4\\u05E2\\u05D1\\u05E8\\u05D4 \\u05DE\\u05D4\\u05D7\\u05E9\\u05D1\\u05D5\\u05DF\n\nWord.Add             = \\u05D4\\u05D5\\u05E1\\u05E4\\u05D4\nWord.All             = \\u05D4\\u05DB\\u05D5\\u05DC\nWord.Balance         = Balance\nWord.Buy             = \\u05E7\\u05E0\\u05D9\\u05D4\nWord.Commodity       = \\u05E1\\u05D7\\u05D5\\u05E8\\u05D4\nWord.Copy            = Copy\nWord.Description     = Description\nWord.Difference      = Difference\nWord.Dividend        = \\u05D3\\u05D9\\u05D1\\u05D9\\u05D3\\u05E0\\u05D3\nWord.Exchange        = Exchange\nWord.Fees            = \\u05E2\\u05DE\\u05DC\\u05D5\\u05EA\nWord.GrossExpense    = Gross Expense\nWord.GrossIncome     = Gross Income\nWord.Interest        = \\u05E8\\u05D9\\u05D1\\u05D9\\u05EA\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = \\u05E2\\u05D1\\u05D5\\u05E8\nWord.Monthly         = \\u05DC\\u05D7\\u05D5\\u05D3\\u05E9\nWord.Name            = \\u05E9\\u05DD\nWord.NetIncome       = \\u05D4\\u05DB\\u05E0\\u05E1\\u05D4 \\u05E0\\u05E7\\u05D9\\u05D9\\u05D4\nWord.NetWorth        = \\u05E9\\u05D5\\u05D5\\u05D9 \\u05E0\\u05E7\\u05D9\nWord.NewBudget       = New Budget\nWord.None            = \\u05D0\\u05E3 \\u05DC\\u05D0 \\u05D0\\u05D7\\u05D3\nWord.Quarterly       = \\u05E8\\u05D1\\u05E2\\u05D5\\u05E0\\u05D9\nWord.ReInvDiv        = Reinvest Dividend\nWord.Remove          = \\u05D4\\u05E1\\u05E8\\u05D4\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Security\nWord.Sell            = \\u05DE\\u05DB\\u05D9\\u05E8\\u05D4\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = \\u05E1\\u05D4\"\\u05DB\nWord.Totals          = Totals\nWord.Yearly          = \\u05E9\\u05E0\\u05EA\\u05D9\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_lt.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Aktyvai\nAccountType.Bank             = Bankas\nAccountType.Cash             = Grynieji\nAccountType.Checking         = Checking\nAccountType.Credit           = Kreditas\nAccountType.Equity           = Akcijos (be pastovi\\u0173 pal\\u016Bkan\\u0173)\nAccountType.Expense          = I\\u0161laidos\nAccountType.Income           = Pajamos\nAccountType.Investment       = Investicijos\nAccountType.Liability        = \\u012Esipareigojimai\nAccountType.MoneyMarket      = pinig\\u0173 rinkos\nAccountType.Mutual           = Bendras kapitalas\nAccountType.Root             = Pagrindin\\u0117\nAccountType.SimpleInvestment = Paprasta investicij\\u0173\n\nButton.AccTerms                = Naudoti buhalterijos terminologij\\u0105\nButton.Accounts                = S\\u0105skaitos\nButton.AckSel                  = Pripa\\u017Einti Pasirinkti\nButton.Add                     = Sukurti\nButton.AllDates                = Visos datos\nButton.Amortize                = Nusid\\u0117v\\u0117ti\nButton.AnimationsEnabled       = \\u012Ejungti animacijos efektus\nButton.AnyStatus               = Bet kokia b\\u016Bsena\nButton.Apply                   = Pritaikyti\nButton.AssetAccounts           = turto apskait\\u0105\nButton.AutoReconcile           = Automatically Reconcile Split Transactions\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Available Securities\nButton.Back                    = Atgal\nButton.BankAccounts            = Banko s\\u0105skaitos\nButton.BudgetMgr               = biud\\u017Eeto direktorius\nButton.Budgeting               = biud\\u017Eeto\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Atsisakyti\nButton.CheckForUpdates         = Patikrinkite nauj\\u0173 atnaujinimus\nButton.CheckReminders          = Patikrinkite Priminimai\nButton.Clear                   = I\\u0161valyti\nButton.ClearAll                = I\\u0161valyti visus\nButton.Cleared                 = Apmok\\u0117ta\nButton.Close                   = U\\u017Edaryti\nButton.Compare                 = palyginti\nButton.ConcatenateMemos        = Kombainas Memo\nButton.ConfirmReminderDelete   = Patvirtinti ant priminimas i\\u0161trinti\nButton.ConfirmTransDelete      = Patvirtinti, sandorio i\\u0161trinti\nButton.CopyToClip              = Nukopijuoti \\u012F i\\u0161karpin\\u0119\nButton.CreateTimeFile          = Sukurti timestamped fail\\u0105 i\\u0161\\u0117jimo\nButton.CreditAccounts          = conti di credito\nButton.Delete                  = Pa\\u0161alinti\nButton.DeleteAll               = Delete All\nButton.DeleteWeekends          = I\\u0161trinti Savaitgaliais\nButton.DetailSplits            = Show Split Details\nButton.Duplicate               = Dubliuoti\nButton.Edit                    = Edit\nButton.EnableAutoComplete      = \\u012Ejungti automatin\\u012F fraz\\u0117s u\\u017Ebaigim\\u0105\nButton.Enabled                 = Leid\\u017Eiamas\nButton.EndingBalance           = Paskutinysis balansas\nButton.Enter                   = \\u012Evesti\nButton.EnterDaysBefore         = Automatically enter number of days before\nButton.ExcludeFromBudget       = Pa\\u0161alinti i\\u0161 biud\\u017Eet\\u0173\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = I\\u0161laidos\nButton.Export                  = eksportas\nButton.ExportSpreadsheet       = Eksporto Skai\\u010Diuokl\\u0117s\nButton.Filter                  = Filtras\nButton.Finish                  = Pabaiga\nButton.FinishLater             = U\\u017Ebaigti v\\u0117liau\nButton.ForceDefaultCurrency    = J\\u0117gos naudojimas kainora\\u0161\\u010Dio valiuta\nButton.ForceGC                 = Force Garbage Collection\nButton.HTTPAuth                = Reikalauja \\u012Fgalioto pri\\u0117jimo\nButton.Hidden                  = Pasl\\u0117pta\nButton.HideAccount             = Hide Account\nButton.HideLockedAccount       = Sl\\u0117pti u\\u017Erakinta s\\u0105skaitas\nButton.HidePlaceholderAccount  = Sl\\u0117pti placeholder s\\u0105skaitos\nButton.HideZeroBalance         = Sl\\u0117pti nulinis balansas s\\u0105skaitas\nButton.HistoricalFill          = Istorin\\u0117 U\\u017Epildykite\nButton.Horizontal              = Horizontalus\nButton.IncludeSubAccounts      = \\u012Etraukti subrangos abonementai\nButton.IncomeAccounts          = Pajamos\nButton.IncomeAndExpense        = Pajamos ir i\\u0161laidos\nButton.Insert                  = Insert\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Apversti pa\\u017Eym\\u0117jim\\u0105\nButton.Jump                    = Jump\nButton.KeepFridays             = Laikyti penktadienius\nButton.KeepMondays             = Laikyti Mondays\nButton.Landscape               = Landscape\nButton.Last120Days             = Paskutinis 120 dien\\u0173\nButton.Last12Months            = Per pastaruosius 12 m\\u0117nesi\\u0173\nButton.Last30Days              = Paskutines 30 dien\\u0173\nButton.Last60Days              = 60 dien\\u0173\nButton.Last90Days              = Paskutines 90 dien\\u0173\nButton.LiabilityAccounts       = atsakomyb\\u0117s s\\u0105skaitos\nButton.Locked                  = U\\u017Eblokuota\nButton.MatchAccountOnly        = Rungtyn\\u0117s naudojant tik \\u012F specifines operacijas\nButton.MatchAllTrans           = Rungtyni\\u0173 naudojant visus sandorius\nButton.MatchCaseSensitive      = Var\\u017Eybos yra ma\\u017Eosios raid\\u0117s\nButton.Modify                  = Keisti\nButton.MonthlyBalance          = M?nesio Balansas\nButton.New                     = Naujas\nButton.NewEmpty                = nauja Tu\\u0161\\u010Dias\nButton.NewHist                 = New Historical\nButton.NewPayment              = Naujas mok\\u0117jimas\nButton.Next                    = Sekantis\nButton.No                      = No\nButton.NoEndDate               = N\\u0117ra Pabaigos data\nButton.None                    = N\\u0117 vienas\nButton.NotReconciled           = ne Suderinta\nButton.Ok                      = Gerai\nButton.Open                    = Atverti\nButton.OpenLastOnStartup       = Atviras paskutin\\u012F fail\\u0105 paleid\\u017Eiant\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Puslapio formatas\nButton.PlaceHolder             = Antra\\u0161t\\u0117\nButton.Portrait                = Portrait\nButton.Print                   = Spausdinti\nButton.PrintSample             = Atspausdinti pavyzd\\u012F\nButton.Properties              = Skelbim\\u0173\nButton.Reconcile               = Patvirtinti\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = Disable automatic reconciliation\nButton.ReconcileIncomeExpense  = Automatically reconcile Income and Expense Accounts\nButton.Reconciled              = Suderinta\nButton.Refresh                 = Refresh\nButton.RegDate                 = Remember Last Transaction Date\nButton.Register                = Operacij\\u0173 s\\u0105ra\\u0161as\nButton.RegisterFollowsList     = U\\u017Eregistruoti \\u0161i\\u0173 s\\u0105skait\\u0173 s\\u0105ra\\u0161\\u0105\nButton.RememberPassword        = Remember Password\nButton.RemindLater             = Remind Me Later\nButton.Reminders               = Reminders\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Pa\\u0161alinti\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Resize\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Veikia Balansas\nButton.Save                    = I\\\\u0161saugoti\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Pasirinkite\nButton.SelectAll               = Pa\\u017Eym\\u0117ti visus\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Show all Commodities\nButton.ShowEmptyAccounts       = Show Zero Balance Accounts\nButton.ShowPercentValues       = Show Percent Values\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = I\\u0161skaidymai\nButton.Start                   = Startas\nButton.Stop                    = Nutraukti\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Leisti temas\nButton.Timestamp               = Sisteminis laikas\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Update currency exchange rates on startup\nButton.UpdateOnline            = Atnaujinti i\\u0161 tinklo\nButton.UpdateSecuritiesStartup = Update securities on startup\nButton.UseDailyRate            = Naudoti periodin\\u0119 kasdienin\\u0119 norm\\u0105\nButton.UseEncryption           = Naudoti kodavim\\u0105\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Use Long Names\nButton.UseProxy                = Naudoti proxy\nButton.UseRegexForFilter       = Naudokite ?prastus posakius filtrai\nButton.Vertical                = Vertikalus\nButton.Yes                     = Yes\nButton.Zoom                    = Zoom\n\nColumn.Account                        = S\\u0105skaita\nColumn.AccountName                    = S\\u0105skaitos pavadinimas\nColumn.Action                         = Veiksmas\nColumn.Actual                         = Actual\nColumn.Amount                         = Kiekis\nColumn.Approve                        = Approve\nColumn.Balance                        = Balansas\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Apmokestinimas\nColumn.Close                          = Close\nColumn.Clr                            = Pat\nColumn.Code                           = Code\nColumn.Commodity                      = Prek\\u0117\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Kreditas\nColumn.Currency                       = Valiuto\nColumn.Date                           = Data\nColumn.Day                            = Day\nColumn.Debit                          = Debetas\nColumn.Decrease                       = Suma\\u017Einti\nColumn.Deposit                        = Ind\\u0117lis\nColumn.Description                    = Description\nColumn.Due                            = Due\nColumn.Enabled                        = Enabled\nColumn.Entries                        = \\u012Era\\u0161ai\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Exchange Rate\nColumn.Expense                        = I\\u0161laidos\nColumn.Freq                           = Frequency\nColumn.Gain                           = Gain\nColumn.High                           = Auk\\u0161tas\nColumn.Income                         = Pajamos\nColumn.Increase                       = Padidinti\nColumn.Investment                     = Investicija\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Loss\nColumn.Low                            = \\u017Demas\nColumn.Memo                           = Memo\nColumn.MktValue                       = Mkt Value\nColumn.Month                          = Month\nColumn.Num                            = Num\nColumn.Payee                          = Gav\\u0117jas\nColumn.Payment                        = Mok\\u0117jimas\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Kaina\nColumn.Print                          = Spausdinti\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Kiekis\nColumn.Rebate                         = Nuolaidos\nColumn.Receive                        = Gauta\nColumn.ReconciledBalance              = Reconciled Balance\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = I\\u0161leista\nColumn.Timestamp                      = timestamp\nColumn.Total                          = I\\u0161 viso\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Tipas\nColumn.Value                          = Value\nColumn.Volume                         = Vert\\u0117\nColumn.Withdrawal                     = I\\u0161\\u0117mimas\n\nDataStoreType.Bxds = dvejetainis failas\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Kiekis\nItem.CashDeposit    = Ind\\u0117lis grynaisiais\nItem.CashWithdrawal = Gryn\\u0173j\\u0173 i\\u0161\\u0117mimas\nItem.Date           = Data\nItem.EFT            = EFT\nItem.Memo           = Pastaba\nItem.NextNum        = Next #\nItem.Payee          = Gav\\u0117jas\nItem.Trans          = Trans\n\nLabel.AccentColor         = Akcentas spalva:\nLabel.Account             = S\\u0105skaita:\nLabel.AccountCode         = S\\u0105skaitos kodas:\nLabel.AccountNumber       = S\\u0105skaitos numeris:\nLabel.AccountOptions      = S\\u0105skaitos parinktys:\nLabel.AccountSeparator    = Account Separator:\nLabel.AccountStatus       = S\\u0105skaitos b\\u016Bsena:\nLabel.AccountType         = S\\u0105skaitos tipas:\nLabel.Action              = Veiksmas:\nLabel.Amount              = Kiekis:\nLabel.AnIntRate           = Skelbiama pal\\u016Bkan\\u0173 norm\\u0105 (APR):\nLabel.Available           = Galimas:\nLabel.Balance             = Balansas:\nLabel.BankAccount         = Banko s\\u0105skaita:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Base Account:\nLabel.BaseColor           = Bazin\\u0117 spalva:\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = Gryn\\u0173j\\u0173 balansas:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Prek\\u0117:\nLabel.CompDaysPerYear     = Mok\\u0117jimo dien\\u0173 per metus:\nLabel.CompPerTerm         = Mok\\u0117jimo period\\u0173 per metus:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Skai\\u010Dius:\nLabel.CreateCurr          = Sukurti papildom\\u0105 valiut\\u0105:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Valiutos / Prek\\u0117:\nLabel.Currencies          = Valiutos:\nLabel.Currency            = Currency:\nLabel.Current             = Dabartinis:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Location:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Data:\nLabel.DateFormat          = Datos Formatas:\nLabel.DaysPastDue         = Days past due:\nLabel.DefaultCurrency     = Dabartin\\u0117 valiuta:\nLabel.DelaySec            = Periodas (sek.)\nLabel.Description         = Apra\\u0161as:\nLabel.DestAccount         = Koresponduojanti s\\u0105skaita:\nLabel.Difference          = Skirtumas:\nLabel.Dividend            = Dividentai:\nLabel.EndDate             = Pabaigos data:\nLabel.EndOn               = End On:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Pabaigos balansas:\nLabel.EquityAccount       = Akcijos (be pastovi\\u0173 pal\\u016Bkan\\u0173)\nLabel.EscrowPmi           = S\\u0105lyginiai ind\\u0117liai, PMI ir t.t.:\nLabel.Event               = Event:\nLabel.Every               = Every:\nLabel.ExchangeAmount      = Exchange Amount:\nLabel.ExchangeRate        = Valiutos kursas:\nLabel.ExchangedAmount     = Exchanged Amount:\nLabel.Fees                = Mokes\\u010Diai:\nLabel.FeesAccount         = Mokes\\u010Di\\u0173 s\\u0105skaita:\nLabel.FileName            = Bylos pavadinimas:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Pirmos \\u012Fmokos data:\nLabel.FocusColor          = D\\u0117mesys spalva:\nLabel.Format              = Format:\nLabel.Frequency           = Frequency:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Gains:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Auk\\u0161tis:\nLabel.High                = Auk\\u0161tas:\nLabel.Host                = Kompiuterio vardas:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Income Account:\nLabel.InterestAccount     = Pelno dalies s\\u0105skaita:\nLabel.LastOccurrence      = Last Occurrence:\nLabel.Layout              = I\\u0161d\\u0117stymas:\nLabel.Left                = Left:\nLabel.LoanTerm            = Skolinimosi laikas (m\\u0117n.):\nLabel.Low                 = \\u017Demas:\nLabel.MarketValue         = Rinkos vert\\u0117:\nLabel.MaxBackupCount      = Maximum number of backup files to keep:\nLabel.Memo                = Pastabos:\nLabel.Monospace           = Monospace:\nLabel.Name                = Pavadinimas:\nLabel.NetIncome           = Net Income:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Next payment date:\nLabel.Notes               = Pastabos:\nLabel.NumTrans            = Operacij\\u0173 skai\\u010Dius:\nLabel.Number              = Numeris:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Opening Statement Date:\nLabel.OpeningBalance      = Prad\\u017Eios balansas:\nLabel.OrigLoanAmt         = Paskolos suma:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Slapta\\u017Eodis:\nLabel.Path                = Kelias\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = \\u012Emok\\u0173 per metus:\nLabel.Payee               = Gav\\u0117jas:\nLabel.Period              = Period\nLabel.Port                = Prievadas:\nLabel.Prefix              = Prefiksas:\nLabel.Price               = Kaina:\nLabel.Proportional        = Proportional:\nLabel.Quantity            = Kiekis:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = Receiving Account:\nLabel.ReconciledBalance   = Suderintas balansas:\nLabel.RemindLater         = Remind me again after:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Repeat on:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = Resolution:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Skal\\u0117:\nLabel.Securities          = Vertybiniai popieriai:\nLabel.Security            = Vertybiniai popieriai:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Show Empty Accounts\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Prad\\u017Eios data:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Pradin\\u0117 pozicija:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = I\\u0161ra\\u0161o data:\nLabel.StorageType         = Storage Type:\nLabel.Suffix              = Sufiksas:\nLabel.Symbol              = Simbolis:\nLabel.TargetBalance       = Siekiamas balansas:\nLabel.Top                 = Top:\nLabel.Total               = I\\u0161 viso:\nLabel.Transaction         = Transaction:\nLabel.TransferFrom        = Transfer From:\nLabel.TransferTo          = Pervesti \\u012F:\nLabel.Type                = Tipas:\nLabel.Units               = Units:\nLabel.UserName            = Vartotojo vardas:\nLabel.Value               = Value:\nLabel.Verify              = Patikslinkite:\nLabel.VerifyPassword      = Verify Password:\nLabel.Visible             = Matomas:\nLabel.Volume              = Vert\\u0117:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Files\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Files\n\nMenu.About.Name                       = _Apie\\u2026\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = S\\u0105skaita\nMenu.AccountRegister.Name             = Account Register\\u2026\nMenu.AddRemoveCurrency.Name           = Sukurti ar pa\\u0161alinti\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Atnaujinti Valiutos\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = Atnaujinti vertybini\\u0173 popieri\\u0173\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balance Sheet\\u2026\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Apmok\\u0117ta\nMenu.Close.Name                       = _U\\u017Edaryti\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = Ko_pijuoti\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _Valiutos\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Pagal _nutyl\\u0117jim\\u0105\\u2026\nMenu.DefaultCurrency.Tooltip          = Change the default currency\nMenu.Delete.Name                      = Pa\\u0161alinti\nMenu.Duplicate.Name                   = Dubliuoti\nMenu.Edit.Name                        = _Taisyti\nMenu.EditTranNumList.Name             = Edit transaction number list\nMenu.Exit.Name                        = _Baigti darb\\u0105\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = _Eksportuoti\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archyvas\\u2026\nMenu.File.Name                        = _Byla\nMenu.Filter.Name                      = _Filtrai\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Pagalba\nMenu.Hide.Name                        = Pasl\\u0117pti\nMenu.HistoryChart.Name                = Pasikeitim\\u0173 istorijos grafikas\\u2026\nMenu.HistoryCommodity.Name            = _Istorija\\u2026\nMenu.HistoryImport.Name               = Importuoti istorij\\u0105\\u2026\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Income / Expense Pie Chart\\u2026\nMenu.Import.Name                      = _Importuoti\nMenu.ImportAccounts.Name              = Import Accounts\\u2026\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = Import jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF\\u2026\nMenu.Jump.Name                        = Jump\nMenu.ListOfAccounts.Name              = S\\u0105sk_ait\\u0173 s\\u0105ra\\u0161as\\u2026\nMenu.Locale.Name                      = Pakeisti vietov\\u0119\\u2026\nMenu.LookAndFeel.Name                 = I\\u0161vaizda\nMenu.MarkAs.Name                      = Mark As\nMenu.Modify.Name                      = Keisti\nMenu.ModifyCommodity.Name             = Su_kurti ar pakeisti\\u2026\nMenu.ModifyCurrency.Name              = _Keisti\\u2026\nMenu.ModifyExchangeRates.Name         = Edit Exchange Rates\\u2026\nMenu.MonthBalance.Name                = M\\u0117nesio balansas\\u2026\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = M\\u0117nesio pabaigos balansas\\u2026\nMenu.MonthEndBalanceCSV.Name          = M\\u0117nesio pabaigos balansas (CSV)\\u2026\nMenu.NetWorth.Name                    = Net Worth\\u2026\nMenu.New.Name                         = _Nauja\\u2026\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Atverti\\u2026\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = Pa_rinktys\\u2026\nMenu.Option.Tooltip                   = Pakeiskite programos savybes\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = Periodin? s?skaitos balansas\\u2026\nMenu.Portfolio.Name                   = Portfolio\\u2026\nMenu.ProfitLoss.Name                  = Profit and Loss\\u2026\nMenu.ProfitLossTXT.Name               = Profit and Loss (Text)\\u2026\nMenu.Reconcile.Name                   = Patvirtinti\nMenu.Reconciled.Name                  = Reconciled\nMenu.RecurringList.Name               = Recurring Transactions\\u2026\nMenu.Register.Name                    = _Registruoti\\u2026\nMenu.Reports.Name                     = _Ataskaitos\nMenu.RunJavaScript.Name               = Run JavaScript\\u2026\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = I\\u0161_saugoti\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = I\\u0161saugoti _kaip\\u2026\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = _Prek\\u0117s\nMenu.SetPassword.Name                 = Priskirti slapta\\u017Eod\\u012F\\u2026\nMenu.Show.Name                        = Rodyti\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Temos\nMenu.Tools.Name                       = P_riemon\\u0117s\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Unreconciled\nMenu.View.Name                        = _Rodyti\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = J\\u016Bs perskait\\u0117te, supratote ir sutinkate su licenzijoje pamin\\u0117tomis s\\u0105lygomis.\nMessage.AccountAdd                   = S\\u0105skaita sukurta\nMessage.AccountCode                  = S\\u0105skaitos kodas ne unikalus: kodas nepakeistas\nMessage.AccountLocked                = S\\u0105skaita u\\u017Eblokuota\nMessage.AccountModify                = S\\u0105skaita pakeista\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = S\\u0105skaita pa\\u0161alinta\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = AutoSave has been turned off\nMessage.AutoSaveOn                   = AutoSave has been turned on\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Checking for new recurring events\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Gathering report data\nMessage.CompilingReport              = Compiling report\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Delete the selected transactions?\nMessage.ConfirmReminderDelete        = Delete the selected reminder?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Delete the selected transaction?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Numatoma valiuta pakeista \\u012F:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Sistema startavo\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Unable to create account\nMessage.Error.AccountRemove          = Could not remove account\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Could not add commodity\nMessage.Error.AddCurrency            = Could not add currency\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Create a basic account set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = File was not found\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = The license was not accepted\nMessage.Error.LoadingFile            = Error loading file\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Could not modify commodity\nMessage.Error.ModifyCurrency         = Could not modify currency\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Byla u\\u017Edaryta\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = Could not load report definition\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Numatoma lokal\\u0117 pakeista \\u012F:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Failed to parse the report definition\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = Ruo\\u0161iamasi sustabdymui\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Report modules were loaded successfully\nMessage.ReportWait                   = Please wait, Loading report modules\nMessage.RestartLocale                = Kad pasikeist\\u0173 lokal\\u0117s nustatymai reikia paleisti program\\u0105 i\\u0161 naujo\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = Enter starting and ending dates\nMessage.StoreBackup                  = Saving backup to:\nMessage.StoreComplete                = File store is complete\nMessage.StoreWait                    = Waiting for file store to complete\nMessage.TXTFile                      = Text Files (*.txt)\nMessage.TransactionAccountLocked     = Operacija sukurti nepavyko, nes s\\u0105skaita(-os) u\\u017Eblokuota(-os)\nMessage.TransactionAdd               = Operacija sukurta\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Operacija pa\\u0161alinta\nMessage.TransactionRemoveLocked      = Operacijos pa\\u0161alinti nepavyko, nes s\\u0105skaita(-os) u\\u017Erakinta(-os)\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = Commodity is in use\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Currency is in use\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Banko s\\u0105skaita:\nName.ExpenseAccounts = I\\u0161laidos\nName.IncomeAccounts  = Pajamos\nName.Root            = Pagrindin\\u0117\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = From {0,date,long} To {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Page {0} of {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 Minutes\nPeriod.15Min     = 15 Minutes\nPeriod.1Day      = 1 Day\nPeriod.1Hr       = 1 Hour\nPeriod.2Hr       = 2 Hours\nPeriod.30Min     = 30 Minutes\nPeriod.5Min      = 5 Minutes\nPeriod.8Hr       = 8 Hours\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Daily\nPeriod.Monthly   = Monthly\nPeriod.NextStart = Next time jGnash starts\nPeriod.None      = None\nPeriod.OnlyOnce  = Only once\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Weekly\nPeriod.Yearly    = Yearly\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = Apie\nTab.Accounts        = Accounts\nTab.Adjust          = Korekcija\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Apmokestinimas\nTab.Credit          = Kreditas\nTab.Credits         = Nusipeln\\u0119\nTab.DataEngine      = Duomen\\u0173 saugojimo sistema\nTab.DataProviders   = Data Providers\nTab.Day             = Day\nTab.Debit           = Debetas\nTab.Formats         = Formats\nTab.GPLLicense      = GPL licenzija\nTab.General         = Bendrosios\nTab.LGPLLicense     = LGPL licenzija\nTab.License         = Licenzija\nTab.Month           = Month\nTab.Network         = Network\nTab.None            = None\nTab.Payment         = Mok\\u0117jimas\nTab.Register        = Operacij\\u0173 s\\u0105ra\\u0161as\nTab.Reminders       = Reminders\nTab.Report          = Report\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = Informacija apie sistem\\u0105\nTab.Transfer        = Pervedimas\nTab.Week            = Week\nTab.Year            = Year\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Gains/(Loss)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Investment Fee\nTag.InvestmentCashTransfer = Investment Cash Transfer\nTag.InvestmentFee          = Investment Fee\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Non-Taxable Interest\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = Taxable Interest\nTag.Vat                    = Vat\n\nTitle.About                      = Apie\nTitle.AccountBalance             = Account Balance\nTitle.AccountFilter              = S\\u0105skait\\u0173 filtrai\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Informacija apie s\\u0105skait\\u0105\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = S\\u0105skaitos saugumas\nTitle.AddRemCurr                 = Sukurti ar pa\\u0161alinti valiutas\nTitle.AmortizationSetup          = Nusidu0117vu0117jimo nustatymai\nTitle.Archive                    = Archyvas\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Automatinis i\\u0161saugojimas\nTitle.Available                  = Galimas\nTitle.BackgroundUpdate           = Background Updates\nTitle.BackingStore               = Atsargin\\u0117s kopijos\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Check Designer\nTitle.ChooseAccounts             = Choose Accounts to Create\nTitle.ColVis                     = Stulpelio matomumas\nTitle.Colors                     = Spalvos\nTitle.CommoditiesSecurities      = Prek\\u0117s ir vertybiniai popieriai\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirm\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Sukurti ar pakeisti prekes\nTitle.Credits                    = Kreditas\nTitle.Currencies                 = Valiutos\nTitle.Current                    = Dabartinis\nTitle.CutOffDate                 = Pasirinkite ribin\\u0119 dat\\u0105\nTitle.DatabaseCfg                = Database Configuration\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Debetas\nTitle.DefDefCurr                 = Nustatyti einam\\u0105j\\u0105 valiut\\u0105:\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = Default Behavior\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Rodyti\nTitle.DuplicateTransaction       = Dubliuoti operacija\nTitle.DuplicateTransactionsFound = Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = M?nesio pabaigoje s?skaitos balanso\nTitle.EnterPassword              = \\u012Eveskite slapta\\u017Eod\\u012F\nTitle.Entry                      = Entry\nTitle.Error                      = Error\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Keitimo kursas\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filtrai\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Default Fonts\nTitle.Frequency                  = Frequency\nTitle.HTTPProxy                  = HTTP proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Pasikeitim\\u0173 istorijos importas\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Importuoti QIF bylos dal\\u012F\nTitle.ImpSum                     = Importuoti s\\u0105ntrauk\\u0105\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = Informacija\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = Keisti QIF Operacijas\nTitle.ModifyAccount              = Keisti s\\u0105skait\\u0105\nTitle.ModifyCurrencies           = Keisti valiutas\nTitle.ModifyReminder             = Keisti Reminder\nTitle.ModifySecHistory           = Pakeisti vertybini\\u0173 popieri\\u0173 istorij\\u0105\nTitle.ModifyTransaction          = Modify Transaction\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nauja s\\u0105skait\\u0105\nTitle.NewBudget                  = Naujos Biud?eto\nTitle.NewFile                    = Nauja byla\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nauja Reminder\nTitle.NewTrans                   = Nauja operacija\nTitle.Notes                      = Pastabos\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Atverti\nTitle.Options                    = Parinktys\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Puslapio formatas\nTitle.ParentAccount              = Auk\\u0161tesnio lygio s\\u0105skaita\nTitle.PercentDist                = Percent Distribution\nTitle.PercentExpense             = Percent Expense\nTitle.PercentIncome              = Percent Income\nTitle.PleaseWait                 = Please Wait\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Profit and Loss Statement\nTitle.ReconcileSettings          = Suderinimo nuostatos\nTitle.Reminder                   = Reminder\nTitle.Reminders                  = Reminders\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Save As\nTitle.SaveFile                   = I\\u0161saugoti byl\\u0105\nTitle.SecurityHistory            = Vertybini\\u0173 popieri\\u0173 pasikeitimai\nTitle.SelAccount                 = Pasirinkite s\\u0105skait\\u0105\nTitle.SelAvailCurr               = Pa\\u017Eym\\u0117kit galimas valiutas\nTitle.SelDate                    = Pasirinkite dat\\u0105\nTitle.SelDefCurr                 = Pasirinkite numatom\\u0105 valiut\\u0105\nTitle.SelDefLocale               = Pasirinkite numatom\\u0105 lokal\\u0119\nTitle.SelDestAccount             = Pasirinkite koresponduojan\\u010Di\\u0105 s\\u0105skait\\u0105\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Pasirinkite s\\u0105skait\\u0105 akcijom\nTitle.SelFile                    = Pasirinkite byl\\u0105\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = Pasirinkite spalv\\u0105\nTitle.Selected                   = Pa\\u017Eym\\u0117ta\nTitle.Shutdown                   = Shutdown\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Split Transaction\nTitle.Startup                    = Startup\nTitle.Steps                      = \\u017Dingsniai\nTitle.Success                    = S?km?\nTitle.Summary                    = Apibendrinimas\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transaction\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Operacij\\u0173 s\\u0105ra\\u0161as\nTitle.TransactionSetup           = Transaction Setup\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Matomas\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = S\\u0105skait\\u0173 s\\u0105ra\\u0161as\nToolTip.AccountRegister                      = S\\u0105skait\\u0173 s\\u0105ra\\u0161as\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Change column visibility\nToolTip.ConvertSEntry                        = Change this transaction into a Double Entry transaction\nToolTip.DeleteAccount                        = Pa\\u0161alinti s\\u0105skait\\u0105\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Atrinkti s\\u0105skaitas\nToolTip.FilterAccounts                       = Filter by account type\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Change Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = tik sveikieji leid\\u017Eiama\nToolTip.ModifyAccount                        = Keisti s\\u0105skait\\u0105\nToolTip.NewAccount                           = Nauja s\\u0105skaita\nToolTip.PageSetup                            = Page Setup\nToolTip.PrintRegRep                          = Print register report\nToolTip.ReconcileAccount                     = Patvirtinti s\\u0105skait\\u0105\nToolTip.Reminders                            = Reminders\nToolTip.ResizeColumns                        = Resize Columns\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Skaitmen\\u0173 \\u012F de\\u0161imtainio teis\\u0119 skai\\u010Dius\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = U\\u017Edarant byl\\u0105 sukurti atsargin\\u0119 kopij\\u0105 su sisteminiu laiku\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Open a new account register\n\nTransaction.AddShare        = Prid\\u0117ti akcijas\nTransaction.BuyShare        = Pirkti akcijas\nTransaction.Dividend        = Dividentai\nTransaction.DoubleEntry     = Dvigubas \\u012Fra\\u0161as\nTransaction.MergeShare      = Akcij\\u0173 apjungimas\nTransaction.ReinvestDiv     = Perinvestuoti dividentus\nTransaction.RemoveShare     = Pa\\u0161alinti akcijas\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Parduoti akcijas\nTransaction.SingleEntry     = Vientisas \\u012Fra\\u0161as\nTransaction.Split           = I\\u0161skaidyti operacij\\u0105\nTransaction.SplitEntry      = I\\u0161skaidyti operacijos \\u012Fra\\u0161\\u0105\nTransaction.SplitShare      = Akcij\\u0173 i\\u0161skaidymas\nTransaction.TransferIn      = Pervedimas \\u012F\nTransaction.TransferOut     = Pervedimas i\\u0161\n\nWord.Add             = Sukurti\nWord.All             = All\nWord.Balance         = Balance\nWord.Buy             = Pirkti\nWord.Commodity       = Prek\\u0117\nWord.Copy            = Copy\nWord.Description     = Description\nWord.Difference      = Difference\nWord.Dividend        = Pal\\u016Bkanos\nWord.Exchange        = Exchange\nWord.Fees            = Mokes\\u010Diai\nWord.GrossExpense    = Gross Expense\nWord.GrossIncome     = Gross Income\nWord.Interest        = Pelno dalis\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = eiti\nWord.Monthly         = iki m\\u0117nesio\nWord.Name            = vardas\nWord.NetIncome       = Net Income\nWord.NetWorth        = Net Worth\nWord.NewBudget       = New Budget\nWord.None            = Nieko\nWord.Quarterly       = Quarterly\nWord.ReInvDiv        = Investuoti pal\\u016Bkanas\nWord.Remove          = Pa\\u0161alinti\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Vertybiniai popieriai\nWord.Sell            = Parduoti\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totals\nWord.Yearly          = Yearly\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_nl.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Activa\nAccountType.Bank             = Bank\nAccountType.Cash             = Cash\nAccountType.Checking         = Zicht\nAccountType.Credit           = Credit\nAccountType.Equity           = Equity\nAccountType.Expense          = Uitgave\nAccountType.Income           = Inkomsten\nAccountType.Investment       = Investering\nAccountType.Liability        = Schulden\nAccountType.MoneyMarket      = Geldmarkt\nAccountType.Mutual           = Mutual Fund\nAccountType.Root             = Root\nAccountType.SimpleInvestment = Simple Investment\n\nButton.AccTerms                = Gebruik grootboekbegrippen\nButton.Accounts                = Rekeningen\nButton.AckSel                  = Selectie bevestigen\nButton.Add                     = Toevoegen\nButton.AllDates                = alle datums\nButton.Amortize                = Afschrijving\nButton.AnimationsEnabled       = Inschakelen animatie-effecten\nButton.AnyStatus               = elke Status\nButton.Apply                   = Toepassen\nButton.AssetAccounts           = Asset Accounts\nButton.AutoReconcile           = Gesplitste transacties automatisch verwerken\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = Beschikbare aandelen\nButton.Back                    = Terug\nButton.BankAccounts            = Bankrekeningen\nButton.BudgetMgr               = budget Manager\nButton.Budgeting               = budgettering\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Annuleren\nButton.CheckForUpdates         = Controleren of er nieuwe updates\nButton.CheckReminders          = Nakijken herinneringen\nButton.Clear                   = Wissen\nButton.ClearAll                = Alles Wissen\nButton.Cleared                 = Bevestigd\nButton.Close                   = Sluiten\nButton.Compare                 = Vergelijken\nButton.ConcatenateMemos        = Combineer Memo\nButton.ConfirmReminderDelete   = Bevestig het verwijderen van herinnering\nButton.ConfirmTransDelete      = Bevestig het verwijderen van transactie\nButton.CopyToClip              = Copi\\u00EEr naar Clipboard\nButton.CreateTimeFile          = Maak een met tijdswaarde aangeduid bestand bij afsluiten\nButton.CreditAccounts          = credit Accounts\nButton.Delete                  = Verwijder\nButton.DeleteAll               = Verwijder alles\nButton.DeleteWeekends          = Verwijder weekends\nButton.DetailSplits            = Toon details van opsplitsingen\nButton.Duplicate               = Dupliceer\nButton.Edit                    = Wijzig\nButton.EnableAutoComplete      = Automatisch aanvullen inschakelen\nButton.Enabled                 = Ingeschakeled\nButton.EndingBalance           = Eindbalans\nButton.Enter                   = Ingeven\nButton.EnterDaysBefore         = Automatisch aantal voorafgaande dagen ingeven\nButton.ExcludeFromBudget       = Uitsluiten van Begrotingen\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Uitgaven rekening\nButton.Export                  = Exporteren\nButton.ExportSpreadsheet       = Export Spreadsheet\nButton.Filter                  = Filter\nButton.Finish                  = Be\\u00EBindig\nButton.FinishLater             = Be\\u00EBindig later\nButton.ForceDefaultCurrency    = Force gebruik van Standaard valuta\nButton.ForceGC                 = Verplicht Opkuis Geheugen\nButton.HTTPAuth                = Authentificatie nodig\nButton.Hidden                  = Verborgen\nButton.HideAccount             = Verberg rekening\nButton.HideLockedAccount       = Verberg gesloten accounts\nButton.HidePlaceholderAccount  = Hide placeholder accounts\nButton.HideZeroBalance         = Hide saldo van nul rekeningen\nButton.HistoricalFill          = historische Fill\nButton.Horizontal              = Horizontaal\nButton.IncludeSubAccounts      = Sub-Rekeningen Toevoegen\nButton.IncomeAccounts          = Inkomsten rekeningen\nButton.IncomeAndExpense        = Inkomsten en uitgaven\nButton.Insert                  = Invoegen\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Selectie omdraaien\nButton.Jump                    = Spring\nButton.KeepFridays             = Houd vrijdag\nButton.KeepMondays             = Houd maandag\nButton.Landscape               = Landscape\nButton.Last120Days             = Laatste 120 Dagen\nButton.Last12Months            = Afgelopen 12 maanden\nButton.Last30Days              = Laatste 30 dagen\nButton.Last60Days              = Laatste 60 Dagen\nButton.Last90Days              = Laatste 90 Dagen\nButton.LiabilityAccounts       = aansprakelijkheid Accounts\nButton.Locked                  = Geblokkeerd\nButton.MatchAccountOnly        = Match met alleen rekening specifieke transacties\nButton.MatchAllTrans           = Pas met behulp van alle transacties\nButton.MatchCaseSensitive      = Match is hoofdlettergevoelig\nButton.Modify                  = Wijzig\nButton.MonthlyBalance          = Maandelijkse Balance\nButton.New                     = Nieuw\nButton.NewEmpty                = nieuw leeg\nButton.NewHist                 = New Historical\nButton.NewPayment              = Nieuwe betaling\nButton.Next                    = Volgende\nButton.No                      = Nee\nButton.NoEndDate               = Geen einddatum\nButton.None                    = Geen\nButton.NotReconciled           = niet Reconciled\nButton.Ok                      = OK\nButton.Open                    = Open\nButton.OpenLastOnStartup       = Open laatste bestand bij het opstarten\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Paginaopmaak\nButton.PlaceHolder             = Opvulling\nButton.Portrait                = Portrait\nButton.Print                   = Afdrukken\nButton.PrintSample             = Afdrukvoorbeeld\nButton.Properties              = eigenschappen\nButton.Reconcile               = Verwerk (reconcile)\nButton.ReconcileBoth           = Alle transactierekeningen hebben dezelfde reconciled status\nButton.ReconcileDisable        = Schakel automatisch verwerken uit\nButton.ReconcileIncomeExpense  = Verwerk Inkomsten en Uitgaven rekeningen automatisch\nButton.Reconciled              = Verwerkt\nButton.Refresh                 = Verversen\nButton.RegDate                 = Onthoud laatste transactie datum\nButton.Register                = Registreren\nButton.RegisterFollowsList     = Invoer volgt rekeningenlijst\nButton.RememberPassword        = Onthoud wachtwoord\nButton.RemindLater             = Herinner me later\nButton.Reminders               = Herinneringen\nButton.RemoteServer            = Remote Server\nButton.Remove                  = Verwijder\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Grootte aanpassen\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Huidige Saldo\nButton.Save                    = Bewaar\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Selecteer\nButton.SelectAll               = Selecteer alles\nButton.SelectText              = Select Text on Focus\nButton.ShowCommodities         = Toon alle artikelen\nButton.ShowEmptyAccounts       = Toon lege rekeningen\nButton.ShowPercentValues       = Laat percentages zien\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Opsplitsingen\nButton.Start                   = Begin\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Thema's ingeschakeld\nButton.Timestamp               = Tijdstempel\nButton.Today                   = Today\nButton.UpdateCurrenciesStartup = Wisselkoersen up-to-date brengen bij opstarten\nButton.UpdateOnline            = Online up-to-date brengen\nButton.UpdateSecuritiesStartup = Koersen aandelen up-to-date brengen bij opstarten\nButton.UseDailyRate            = Gebruik dagwaarde (Daily Periodic Rate)\nButton.UseEncryption           = Gebruik versleuteling\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Gebruik lange namen\nButton.UseProxy                = Gebruik proxy\nButton.UseRegexForFilter       = Gebruik reguliere expressies voor filters\nButton.Vertical                = Verticaal\nButton.Yes                     = Ja\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Rekeningen\nColumn.AccountName                    = Rekeningnaam\nColumn.Action                         = Actie\nColumn.Actual                         = Actual\nColumn.Amount                         = Hoeveelheid\nColumn.Approve                        = Goedkeuren\nColumn.Balance                        = Balans\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Kosten\nColumn.Close                          = Close\nColumn.Clr                            = Clr\nColumn.Code                           = Code\nColumn.Commodity                      = Artikel\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Credit\nColumn.Currency                       = Valuta\nColumn.Date                           = Datum\nColumn.Day                            = Dag\nColumn.Debit                          = Debit\nColumn.Decrease                       = Verminderen\nColumn.Deposit                        = Inleg\nColumn.Description                    = Beschrijving\nColumn.Due                            = Due\nColumn.Enabled                        = Ingeschakeld\nColumn.Entries                        = Regels\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Wisselkoers\nColumn.Expense                        = Uitgave\nColumn.Freq                           = Frequentie\nColumn.Gain                           = Winst\nColumn.High                           = Hoog\nColumn.Income                         = Inkomsten\nColumn.Increase                       = Vermeerderen\nColumn.Investment                     = Investering\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Verlies\nColumn.Low                            = Laag\nColumn.Memo                           = Memo\nColumn.MktValue                       = Waarde\nColumn.Month                          = Maand\nColumn.Num                            = Num\nColumn.Payee                          = Begunstigde\nColumn.Payment                        = Betaling\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Prijs\nColumn.Print                          = Afdrukken\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Hoeveelheid\nColumn.Rebate                         = Teruggave\nColumn.Receive                        = Ontvangen\nColumn.ReconciledBalance              = Verwerkt Saldo\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Aandeel\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Aant.\nColumn.Short.RealizedGain             = G Winst\nColumn.Short.RealizedGainPercentage   = G Winst %\nColumn.Short.TotalGain                = T Winst\nColumn.Short.TotalGainPercentage      = T Winst %\nColumn.Short.UnrealizedGain           = O Winst\nColumn.Short.UnrealizedGainPercentage = O Winst %\nColumn.Spend                          = Uitgeven\nColumn.Timestamp                      = Timestamp\nColumn.Total                          = Totaal\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Soort\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Opname\n\nDataStoreType.Bxds = Binair bestand\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML File\n\nItem.Amount         = Hoeveelheid\nItem.CashDeposit    = Cash storting\nItem.CashWithdrawal = Cash opname\nItem.Date           = Datum\nItem.EFT            = EFT\nItem.Memo           = Memo\nItem.NextNum        = Volgende nr.\nItem.Payee          = Begunstigde\nItem.Trans          = Trans\n\nLabel.AccentColor         = Accent kleur:\nLabel.Account             = Rekening:\nLabel.AccountCode         = Account Code:\nLabel.AccountNumber       = Rekeningnummer:\nLabel.AccountOptions      = Rekeningopties:\nLabel.AccountSeparator    = Rekeningscheidingsteken:\nLabel.AccountStatus       = Rekeningstatus:\nLabel.AccountType         = Rekeningtype:\nLabel.Action              = Actie:\nLabel.Amount              = Hoeveelheid:\nLabel.AnIntRate           = Jaarlijkse rente percentage (APR):\nLabel.Available           = Beschikbaar:\nLabel.Balance             = Balans:\nLabel.BankAccount         = Bankrekening:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Basisrekening:\nLabel.BaseColor           = Basis Kleur:\nLabel.Bottom              = Bottom:\nLabel.By                  = Door:\nLabel.CashBalance         = Cash Balans:\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = Artikel:\nLabel.CompDaysPerYear     = Compounding Dagen per Jaar:\nLabel.CompPerTerm         = Compounding Periodes per Jaar:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = Aantal:\nLabel.CreateCurr          = Cre\\u00EBer eigen valuta:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Valuta / Artikel:\nLabel.Currencies          = Valuta:\nLabel.Currency            = Munt:\nLabel.Current             = Huidig:\nLabel.DatabaseBackend     = Database Backend:\nLabel.DatabaseName        = Database Locatie:\nLabel.DatabaseServer      = Database Server:\nLabel.Date                = Datum:\nLabel.DateFormat          = Datumformaat:\nLabel.DaysPastDue         = Dagen overtijd:\nLabel.DefaultCurrency     = Standaard valuta:\nLabel.DelaySec            = Vertraging (seconden)\nLabel.Description         = Beschrijving:\nLabel.DestAccount         = Doelrekening:\nLabel.Difference          = Verschil:\nLabel.Dividend            = Dividend:\nLabel.EndDate             = Einddatum:\nLabel.EndOn               = Eindigt op:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Eindbalans:\nLabel.EquityAccount       = Kapitaalrekening (equity account):\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Elke:\nLabel.ExchangeAmount      = Te Wisselen Bedrag:\nLabel.ExchangeRate        = Wisselkoers:\nLabel.ExchangedAmount     = Gewisseld Bedrag:\nLabel.Fees                = Kosten:\nLabel.FeesAccount         = Kostenrekening:\nLabel.FileName            = Bestandsnaam:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Datum van eerste betaling:\nLabel.FocusColor          = Focus Kleur:\nLabel.Format              = Format:\nLabel.Frequency           = Frequentie:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Winsten:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Hoogte:\nLabel.High                = Hoog:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Inkomstenrekening:\nLabel.InterestAccount     = Renterekening:\nLabel.LastOccurrence      = Laatste voorkomen:\nLabel.Layout              = Indeling:\nLabel.Left                = Left:\nLabel.LoanTerm            = Looptijd lening (in maanden):\nLabel.Low                 = Laag:\nLabel.MarketValue         = Marktwaarde:\nLabel.MaxBackupCount      = Maximum aantal backups:\nLabel.Memo                = Memo:\nLabel.Monospace           = Monospace:\nLabel.Name                = Naam:\nLabel.NetIncome           = Netto inkomsten:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Volgende betalingsdatum:\nLabel.Notes               = Opmerkingen:\nLabel.NumTrans            = Aantal transacties:\nLabel.Number              = Nummer:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Datum van opening:\nLabel.OpeningBalance      = Startbalans:\nLabel.OrigLoanAmt         = Oorspronkelijk leningsbedrag:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Wachtwoord:\nLabel.Path                = Path\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Betalingen per jaar:\nLabel.Payee               = Begunstigde:\nLabel.Period              = Period\nLabel.Port                = Poort:\nLabel.Prefix              = Prefix:\nLabel.Price               = Prijs:\nLabel.Proportional        = Proportioneel:\nLabel.Quantity            = Hoeveelheid:\nLabel.QuoteSource         = Koersbron:\nLabel.ReceivingAccount    = Ontvangende Rekening:\nLabel.ReconciledBalance   = Verwerkte balans (reconciled):\nLabel.RemindLater         = Herinner me weer na:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Herhaal op:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Gerapporteerde Munteenheid:\nLabel.Resolution          = Resolutie:\nLabel.ReturnOfCapital     = Return of Capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Schaal:\nLabel.Securities          = Aandelen:\nLabel.Security            = Beveiliging:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Laat lege rekeningen zien\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Startdatum:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Startpositie:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Afschriftdatum:\nLabel.StorageType         = Type Opslag:\nLabel.Suffix              = Achtervoegsel:\nLabel.Symbol              = Symbool:\nLabel.TargetBalance       = Doelbalans:\nLabel.Top                 = Top:\nLabel.Total               = Totaal:\nLabel.Transaction         = Transactie:\nLabel.TransferFrom        = Boek van:\nLabel.TransferTo          = Boek naar:\nLabel.Type                = Soort:\nLabel.Units               = Units:\nLabel.UserName            = Gebruikersnaam:\nLabel.Value               = Value:\nLabel.Verify              = Verifieren:\nLabel.VerifyPassword      = Verifieer wachtwoord:\nLabel.Visible             = Zichtbaar:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML Bestanden\nLabel.Year                = Year:\nLabel.jGnashFiles         = jGnash Bestanden\n\nMenu.About.Name                       = O_ver\\u2026\nMenu.About.Tooltip                    = Informatie over jGnash\nMenu.Account.Name                     = Rekening\nMenu.AccountRegister.Name             = Grootboek...\nMenu.AddRemoveCurrency.Name=_Toevoegen / Verwijderen\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Valuta-update\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = update Securities\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = Balans...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Bevestigd\nMenu.Close.Name                       = Sl_uiten\nMenu.Close.Tooltip                    = Sluit huidige bestand\nMenu.CloseAllWindows.Name             = Close all windows\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = K_opieer\nMenu.Copy.Tooltip                     = Maak een kopie van het geselecteerde item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _Valuta\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Wijzig st_andaard...\nMenu.DefaultCurrency.Tooltip          = Wijzig de standaard valuta\nMenu.Delete.Name                      = Verwijder\nMenu.Duplicate.Name                   = Dupliceer\nMenu.Edit.Name                        = _Wijzig\nMenu.EditTranNumList.Name             = Wijzig transactienummerlijst\nMenu.Exit.Name                        = Afsluit_en\nMenu.Exit.Tooltip                     = jGnash afsluiten\nMenu.Export.Name                      = Expor_t\nMenu.ExportAccounts.Name              = Export Accounts\\u2026\nMenu.File.Archive                     = Archiveer...\nMenu.File.Name                        = _Bestand\nMenu.Filter.Name                      = _Filters\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _Help\nMenu.Hide.Name                        = Verberg\nMenu.HistoryChart.Name                = Historisch diagram...\nMenu.HistoryCommodity.Name            = _Historie ...\nMenu.HistoryImport.Name               = Historische import...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Inkomsten / Uitgaven taartdiagram...\nMenu.Import.Name                      = _Importeer\nMenu.ImportAccounts.Name              = Importeer Rekeningen...\nMenu.ImportAccounts.Tooltip           = Importeer een jGnash Rekeningbestand\nMenu.ImportJgnash.Name                = Importeer jGnash...\nMenu.ImportJgnash.Tooltip             = Importeer jGnash 1.11.x bestanden\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Importeer SWIFT MT940 bestanden\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Importeren OFX and QFX bestanden\nMenu.ImportQif.Name                   = _QIF...\nMenu.Jump.Name                        = Spring\nMenu.ListOfAccounts.Name              = _Rekeningenlijst\\u2026\nMenu.Locale.Name                      = Wijzig Locale...\nMenu.LookAndFeel.Name                 = Look and Feel\nMenu.MarkAs.Name                      = Markeer als\nMenu.Modify.Name                      = Wijzig\nMenu.ModifyCommodity.Name             = _Cree\\u00EBr / Modificeer ...\nMenu.ModifyCurrency.Name              = _Modificeer...\nMenu.ModifyExchangeRates.Name         = Aanpassen Wisselkoersen...\nMenu.MonthBalance.Name                = Maandelijkse Balans...\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = Eind-van-de-maand Balans...\nMenu.MonthEndBalanceCSV.Name          = Eind-van-de-maand Balans (CSV)...\nMenu.NetWorth.Name                    = Netto Waarde...\nMenu.New.Name                         = _Nieuw\\u2026\nMenu.New.Tooltip                      = Maak een nieuw bestand aan\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Open\\u2026\nMenu.Open.Tooltip                     = Open dit bestand\nMenu.Option.Name                      = _Opties...\nMenu.Option.Tooltip                   = Wijzig programmaopties\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Income / Expense by Payee Pie Chart\\u2026\nMenu.PeriodicAccountBalance.Name      = Periodieke Rekeningsaldo\\u2026\nMenu.Portfolio.Name                   = Portfolio...\nMenu.ProfitLoss.Name                  = Winst en verlies...\nMenu.ProfitLossTXT.Name               = Winst en verlies (Text)...\nMenu.Reconcile.Name                   = Reconcile\nMenu.Reconciled.Name                  = Reconciled\nMenu.RecurringList.Name               = Terugkerende Transacties...\nMenu.Register.Name                    = _Registreer...\nMenu.Reports.Name                     = _Rapporten\nMenu.RunJavaScript.Name               = Uitvoeren JavaScript...\nMenu.RunJavaScript.Tooltip            = Uitvoeren JavaScript\nMenu.Save.Name                        = _Bewaar\nMenu.Save.Tooltip                     = Bewaar het huidige bestand\nMenu.SaveAs.Name                      = Bewaar _als...\nMenu.SaveAs.Tooltip                   = Bewaar het huidige besand onder een nieuwe naam\nMenu.Securities.Name                  = A_rtikelen\nMenu.SetPassword.Name                 = Stel wachtwoord in...\nMenu.Show.Name                        = Toon\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Thema's\nMenu.Tools.Name                       = _Tools\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Niet verwerkt (Unreconciled)\nMenu.View.Name                        = _View\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = Ik heb de voorwaarden van de licenties gelezen en accepteer ze.\nMessage.AccountAdd                   = Rekening toegevoegd\nMessage.AccountCode                  = Rekeningcode was niet uniek; de code is niet gewijzigd.\nMessage.AccountLocked                = Rekekening is geblokkeerd\nMessage.AccountModify                = Rekening gewijzigd\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = Rekening verwijderd\nMessage.AntiAlias                    = Activeer text antialiasing!\nMessage.AutoSaveOff                  = AutoSave is nu uitgeschakeld\nMessage.AutoSaveOn                   = AutoSave is nu ingeschakeld\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = Controleren op terugkerende gebeurtenissen\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Ophalen rapport-data\nMessage.CompilingReport              = Aanmaken rapport\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = De geselecteerde transacties verwijderen?\nMessage.ConfirmReminderDelete        = De geselecteerde herinnering verwijderen?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = De geselcteerde transactie verwijderen?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Standaard valuta gewijzigd in:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Engine gestart\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = Kon rekening niet aanmaken\nMessage.Error.AccountRemove          = Kon rekening niet verwijderen\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = Kon artikel niet toevoegen\nMessage.Error.AddCurrency            = Kon valuta niet toevoegen\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Cree\\u00EBr eerst een basis rekeningenset (dit mag een heel eenvoudige zijn, hoeft niet eens te lijken op de te importeren rekeningenset)\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = Bestand niet gevonden\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = De licentie is niet geaccepteerd\nMessage.Error.LoadingFile            = Fout bij het laden van het bestand\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = Kon artikel niet wijzigen\nMessage.Error.ModifyCurrency         = Kon valuta niet wijzigen\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Geen enkele transactie verwerkt\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Bestand gesloten\nMessage.FileIsLocked                 = Het bestand is vergrendeld door een andere toepassing\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = Het geselecteerde bestand is niet geldig\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Gelieve te wachten, deze import kan even duren\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash heeft minstens een 11 JVM nodig\nMessage.LoadReportFail               = Kon rapportdefinitie niet laden\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Standaard locale gewijzigd in:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = Herhaalt niet\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = De bestaande database wordt overschreven\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = Fout bij het uitpluizen van de rapportdefinitie (parse error)\nMessage.PleaseWait                   = Even wachten\nMessage.PrefFail                     = Geen oude voorkeuren gevonden\nMessage.PrepShutdown                 = Voorbereiden voor afsluiten\nMessage.ProcessingReportData         = Verwerken rapport-data\nMessage.Proxy                        = http proxy instellen:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Rapportmodules zijn succesvol geladen\nMessage.ReportWait                   = Wachten, rapportmodules worden geladen\nMessage.RestartLocale                = Herstart om de wijziging in locale te activeren\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Bezig met zoeken, gelieve te wachten\nMessage.Shutdown                     = Afsluiten\nMessage.StartEndDate                 = Vul startdatum en einddatum in\nMessage.StoreBackup                  = Reservekopie wordt geschreven naar:\nMessage.StoreComplete                = Bestandsopslag is voltooid\nMessage.StoreWait                    = Wacht op voltooien bestandsopslag\nMessage.TXTFile                      = Tekstbestanden (*.txt)\nMessage.TransactionAccountLocked     = Toevoegen van transactie is mislukt, de doelrekening(en) is/zijn geblokkeerd\nMessage.TransactionAdd               = Transactie toegevoegd\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transactie verwijderd\nMessage.TransactionRemoveLocked      = Het verwijderen van de transactie is mislukt, de doelrekening(en) is/zijn geblokkeerd\nMessage.UninstallBad                 = Fout bij het verwijderen van de applicatie\nMessage.UninstallGood                = Verwijderen gelukt!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = U gebruikt versie\nMessage.Warn.CommodityInUse          = Artikel is in gebruik\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = Valuta is in gebruik\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Bankrekeningen\nName.ExpenseAccounts = Uitgaven\nName.IncomeAccounts  = Inkomsten\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Van {0,date,long} tot {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Bladzijde {0} of {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 minuten\nPeriod.15Min     = 15 minuten\nPeriod.1Day      = 1 dag\nPeriod.1Hr       = 1 uur\nPeriod.2Hr       = 2 uren\nPeriod.30Min     = 30 minuten\nPeriod.5Min      = 5 minuten\nPeriod.8Hr       = 8 uren\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Dagelijks\nPeriod.Monthly   = Maandelijks\nPeriod.NextStart = De volgende keer dat jGnash opstart\nPeriod.None      = Geen\nPeriod.OnlyOnce  = Eenmalig\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Wekelijks\nPeriod.Yearly    = Jaarlijks\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = Geen\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = Over\nTab.Accounts        = Accounts\nTab.Adjust          = Instellen\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Belast\nTab.Credit          = Credit\nTab.Credits         = Credits\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = Dag\nTab.Debit           = Debit\nTab.Formats         = Formats\nTab.GPLLicense      = GPL Licentie\nTab.General         = Algemeen\nTab.LGPLLicense     = LGPL Licentie\nTab.License         = Licentie\nTab.Month           = Maand\nTab.Network         = Network\nTab.None            = Geen\nTab.Payment         = Betaling\nTab.Register        = Grootboek\nTab.Reminders       = Herinneringen\nTab.Report          = Rapport\nTab.StartupShutdown = Opstarten / Afsluiten\nTab.SysInfo         = Systeeminformatie\nTab.Transfer        = Boeking\nTab.Week            = Week\nTab.Year            = Jaar\n\nTag.Bank                   = Bank\nTag.Dividend               = Dividend\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Winsten/(Verliezen)\nTag.GainsOffset            = Winsten Offset\nTag.Investment             = Investeringskost\nTag.InvestmentCashTransfer = Investering Cash Overboeking\nTag.InvestmentFee          = Investeringskost\nTag.LTCG                   = Lange Termijn Kapitaalwinst Verdeling\nTag.MTCG                   = Gemiddelde Termijn Kapitaalwinst Verdeling\nTag.Misc                   = Alg.\nTag.NonTaxableInterest     = Niet-taxeerbare Intrest\nTag.STCG                   = Korte Termijn Kapitaalwinst Verdeling\nTag.TaxableInterest        = Taxeerbare Intrest\nTag.Vat                    = BTW\n\nTitle.About                      = Over\nTitle.AccountBalance             = Rekening balans\nTitle.AccountFilter              = Rekening filters\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Rekening informatie\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Rekening aandelen\nTitle.AddRemCurr                 = Voeg toe/verwijder valuta\nTitle.AmortizationSetup          = Afschrijving instellingen\nTitle.Archive                    = Archiveer\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Automatisch opslaan\nTitle.Available                  = Beschikbaar\nTitle.BackgroundUpdate           = Updates op de Achtergrond\nTitle.BackingStore               = Onderliggend Bestand\nTitle.BalanceSheet               = Balance Sheet\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Cheque ontwerp\nTitle.ChooseAccounts             = Selecteer aan te maken Rekeningen\nTitle.ColVis                     = Zichtbaarheid kolom\nTitle.Colors                     = Kleuren\nTitle.CommoditiesSecurities      = Aandelen\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Bevestig\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = Cre\\u00EBer / Wijzig artikelen\nTitle.Credits                    = Credits\nTitle.Currencies                 = Valuta's\nTitle.Current                    = Huidig\nTitle.CutOffDate                 = Selecteer dagovergang\nTitle.DatabaseCfg                = Database Configuratie\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Debits\nTitle.DefDefCurr                 = Definieer standaard valuta\nTitle.DefTranNum                 = Standaard transactienummering\nTitle.DefaultBehavior            = Standaard gedrag (behavior)\nTitle.Defaults                   = Standaards (default)\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Tonen\nTitle.DuplicateTransaction       = Dupliceer transactie\nTitle.DuplicateTransactionsFound = Dupliceer de gevonden transactie\nTitle.EditExchangeRates          = Editeer Wisselkoersen\nTitle.EndMonthBalance            = Eind-van-de-maand rekeningbalans\nTitle.EnterPassword              = Typ wachtwoord\nTitle.Entry                      = Invoer\nTitle.Error                      = Fout\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Wisselkoers\nTitle.FileImport                 = Selecteer File om te Importeren\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filters\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Standaard Fonts\nTitle.Frequency                  = Frequentie\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Historische import\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Importeer een gedeeltelijke QIF file\nTitle.ImpSum                     = Importsamenvatting\nTitle.ImportOFX                  = Importeer een OFX file\nTitle.ImportTransactions         = Importeer transacties uit bestand\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = informatie\nTitle.InvFees                    = Investeringskosten\nTitle.InvGainsLoss               = Investering Winsten / Verliezen\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Wijzig Transacties\nTitle.ModOFXTrans                = Wijzig OFX Transacties\nTitle.ModQIFTrans                = Wijzig QIF transacties\nTitle.ModifyAccount              = Wijzig rekening\nTitle.ModifyCurrencies           = Wijzig valuta\nTitle.ModifyReminder             = Wijzig herinnering\nTitle.ModifySecHistory           = Wijzig aandelenhistoriek\nTitle.ModifyTransaction          = Wijzig transactie\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nieuwe rekening\nTitle.NewBudget                  = New Budget\nTitle.NewFile                    = Creeer een nieuw bestand\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nieuwe herinnering\nTitle.NewTrans                   = Nieuwe transactie\nTitle.Notes                      = Opmerkingen\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Open\nTitle.Options                    = Opties\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Paginaopmaak\nTitle.ParentAccount              = Bovenliggende rekening\nTitle.PercentDist                = Percentverdeling\nTitle.PercentExpense             = Percent uitgaven\nTitle.PercentIncome              = Percent inkomsten\nTitle.PleaseWait                 = Even geduld\nTitle.PortfolioReport            = Portfolio Rapport\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Winst en verlies statement\nTitle.ReconcileSettings          = \"Verwerk\" instellingen (Reconcile Settings)\nTitle.Reminder                   = Herinnering\nTitle.Reminders                  = Herinneringen\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Bewaar Als\nTitle.SaveFile                   = Bewaar bestand\nTitle.SecurityHistory            = Historiek Aandeel\nTitle.SelAccount                 = Selecteer rekening\nTitle.SelAvailCurr               = Selecteer beschikbare valuta\nTitle.SelDate                    = Selecteer datum\nTitle.SelDefCurr                 = Selecteer standaard valuta\nTitle.SelDefLocale               = Selecteer standaard Locale\nTitle.SelDestAccount             = Selecteer doelrekening\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Selecteer Equity rekening\nTitle.SelFile                    = Selecteer bestand\nTitle.SelQifDateFormat           = Selecteer QIF datum formaat\nTitle.SelectColor                = Selecteer kleur\nTitle.Selected                   = Geselecteerd\nTitle.Shutdown                   = Afsluiten\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Splits Transactie\nTitle.Startup                    = Startup\nTitle.Steps                      = Stappen\nTitle.Success                    = Succes\nTitle.Summary                    = Samenvatting\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transactie\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Transactielijst\nTitle.TransactionSetup           = Transactieinstellingen\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Overwachte fout\nTitle.ViewImage                  = View Image\nTitle.Visible                    = zichtbaar\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Rekeningenlijst\nToolTip.AccountRegister                      = Grootboek\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Wijzig zichtbaarheid kolom\nToolTip.ConvertSEntry                        = Wijzig deze transactie in een tweeregelige transactie\nToolTip.DeleteAccount                        = Verwijder rekening\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filter rekening\nToolTip.FilterAccounts                       = Filter op rekeningtype\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Aanpassen Font Size\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = alleen gehele getallen toegestaan\nToolTip.ModifyAccount                        = Wijzig rekening\nToolTip.NewAccount                           = Nieuwe rekening\nToolTip.PageSetup                            = Pagina Indeling\nToolTip.PrintRegRep                          = Druk grootboekrapport af\nToolTip.ReconcileAccount                     = Verwerk (reconcile) rekening\nToolTip.Reminders                            = Herinneringen\nToolTip.ResizeColumns                        = Kolombreedte aanpassen\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = Aantal decimalen rechts van het decimaal-teken\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Cre\\u00EBer een backup met tijdstempel bij afsluiten\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Open een nieuw grootboek\n\nTransaction.AddShare        = Aandelen Toevoegen\nTransaction.BuyShare        = Aandelen Kopen\nTransaction.Dividend        = Dividend\nTransaction.DoubleEntry     = Dubbele Ingave\nTransaction.MergeShare      = Fusie Aandelen\nTransaction.ReinvestDiv     = Dividend herinvesteren\nTransaction.RemoveShare     = Aandelen verwijderen\nTransaction.ReturnOfCapital = Return of Capital\nTransaction.SellShare       = Aandelen Verkopen\nTransaction.SingleEntry     = Enkele Ingave\nTransaction.Split           = Opgesplitste transactie\nTransaction.SplitEntry      = Opgesplitste transactie invoer\nTransaction.SplitShare      = Aandelensplit\nTransaction.TransferIn      = Inkomende Transfer\nTransaction.TransferOut     = Uitgaande Transfer\n\nWord.Add             = Toevoegen\nWord.All             = Alle\nWord.Balance         = Balans\nWord.Buy             = Kopen\nWord.Commodity       = Valuta\nWord.Copy            = Copy\nWord.Description     = Beschrijving\nWord.Difference      = Verschil\nWord.Dividend        = Dividend\nWord.Exchange        = Exchange\nWord.Fees            = Kosten\nWord.GrossExpense    = Bruto uitgaven\nWord.GrossIncome     = Bruto inkomsten\nWord.Interest        = Rente\nWord.Into            = into\nWord.Invalid         = Invalid\nWord.Merge           = gaan\nWord.Monthly         = Maandelijks\nWord.Name            = naam\nWord.NetIncome       = Netto inkomsten\nWord.NetWorth        = Netto waarde\nWord.NewBudget       = Nieuw budget\nWord.None            = Geen enkele\nWord.Quarterly       = Elk kwartaal\nWord.ReInvDiv        = Dividend Herinvesteren\nWord.Remove          = Verwijder\nWord.ReturnOfCapital = Return of Capital\nWord.Seconds         = seconds\nWord.Security        = Beveiliging\nWord.Sell            = Verkoop\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Totaal\nWord.Totals          = Totals\nWord.Yearly          = Jaarlijks\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_pl.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Aktywa\nAccountType.Bank             = Bank\nAccountType.Cash             = Got\\u00F3wka\nAccountType.Checking         = Czeki\nAccountType.Credit           = Po\\u017Cyczki\nAccountType.Equity           = Kapita\\u0142\nAccountType.Expense          = Koszty\nAccountType.Income           = Dochody\nAccountType.Investment       = Inwestycje\nAccountType.Liability        = Zobowi\\u0105zania\nAccountType.MoneyMarket      = Lokaty\nAccountType.Mutual           = Fundusze inwestycyjne\nAccountType.Root             = Korze\\u0144\nAccountType.SimpleInvestment = Konto emerytalne\n\nButton.AccTerms                = U\\u017Cywaj formalnego s\\u0142ownictwa ksi\\u0119gowego\nButton.Accounts                = Konta\nButton.AckSel                  = Potwierd\\u017A wybrane\nButton.Add                     = Dodaj\nButton.AllDates                = Wszystkie daty\nButton.Amortize                = Amortyzuj\nButton.AnimationsEnabled       = W\\u0142\\u0105czone efekty animacji\nButton.AnyStatus               = Dowolny status\nButton.Apply                   = Zastosuj\nButton.AssetAccounts           = Konta aktyw\\u00F3w\nButton.AutoReconcile           = Automatycznie uzgadniaj transakcje dzielone\nButton.AutoResizeColumns       = Automatycznie zmieniaj rozmiar kolumn tabeli\nButton.AvailSecurities         = Dost\\u0119pne papiery warto\\u015Bciowe\nButton.Back                    = Wr\\u00F3\\u0107\nButton.BankAccounts            = Konta bankowe\nButton.BudgetMgr               = Zarz\\u0105dzanie bud\\u017Cetami\nButton.Budgeting               = Bud\\u017Cetowanie\nButton.CalcBal                 = Oblicz saldo\nButton.Cancel                  = Anuluj\nButton.CheckForUpdates         = Sprawd\\u017A dost\\u0119pno\\u015B\\u0107 nowych aktualizacji\nButton.CheckReminders          = Sprawd\\u017A transakcje powtarzalne\nButton.Clear                   = Wyczy\\u015B\\u0107\nButton.ClearAll                = Wyczy\\u015B\\u0107 wszystko\nButton.Cleared                 = Rozliczona\nButton.Close                   = Zamknij\nButton.Compare                 = Por\\u00F3wnaj\nButton.ConcatenateMemos        = Po\\u0142\\u0105cz notatki\nButton.ConfirmReminderDelete   = Potwierdzaj usuwanie transakcji powtarzalnej\nButton.ConfirmTransDelete      = Potwierdzaj usuwanie transakcji\nButton.CopyToClip              = Kopiuj do schowka\nButton.CreateTimeFile          = Tw\\u00F3rz kopi\\u0119 zapasow\\u0105 przy wyj\\u015Bciu\nButton.CreditAccounts          = Konta po\\u017Cyczek\nButton.Delete                  = Usu\\u0144\nButton.DeleteAll               = Usu\\u0144 wszystko\nButton.DeleteWeekends          = Usu\\u0144 weekendy\nButton.DetailSplits            = Poka\\u017C szczeg\\u00F3\\u0142y podzia\\u0142u\nButton.Duplicate               = Kopiuj\nButton.Edit                    = Edytuj\nButton.EnableAutoComplete      = W\\u0142\\u0105cz automatyczne uzupe\\u0142nianie\nButton.Enabled                 = W\\u0142\\u0105czone\nButton.EndingBalance           = Saldo ko\\u0144cowe\nButton.Enter                   = Wprowad\\u017A\nButton.EnterDaysBefore         = Automatycznie wprowad\\u017A przed podan\\u0105 liczb\\u0105 dni\nButton.ExcludeFromBudget       = Wyklucz z bud\\u017Cetu\nButton.ExecuteNow              = Wykonaj teraz\nButton.ExpenseAccounts         = Konta koszt\\u00F3w\nButton.Export                  = Eksportuj\nButton.ExportSpreadsheet       = Eksportuj arkusz kalkulacyjny\nButton.Filter                  = Filtr\nButton.Finish                  = Zako\\u0144cz\nButton.FinishLater             = Zako\\u0144cz p\\u00F3\\u017Aniej\nButton.ForceDefaultCurrency    = Wymu\\u015B u\\u017Cycie Waluty Domy\\u015Blnej\nButton.ForceGC                 = Wymu\\u015B zbieranie \\u015Bmieci\nButton.HTTPAuth                = Wymaga uwierzytelnienia\nButton.Hidden                  = Ukryte\nButton.HideAccount             = Ukryj konto\nButton.HideLockedAccount       = Ukryj konta zablokowane\nButton.HidePlaceholderAccount  = Ukryj konta wype\\u0142niacze\nButton.HideZeroBalance         = Ukryj konta z saldem zerowym\nButton.HistoricalFill          = Wype\\u0142nij wg historii\nButton.Horizontal              = Poziomo\nButton.IncludeSubAccounts      = Do\\u0142\\u0105cz konta podrz\\u0119dne\nButton.IncomeAccounts          = Konta dochod\\u00F3w\nButton.IncomeAndExpense        = Doch\\u00F3d i koszt\nButton.Insert                  = Wstaw\nButton.InvertBalances          = Odwr\\u00F3\\u0107 salda\nButton.InvertSelection         = Odwr\\u00F3\\u0107 zaznaczenie\nButton.Jump                    = Skocz\nButton.KeepFridays             = Zachowaj pi\\u0105tki\nButton.KeepMondays             = Zachowaj poniedzia\\u0142ki\nButton.Landscape               = Landscape\nButton.Last120Days             = Ostatnie 120 dni\nButton.Last12Months            = Ostatnie 12 mies.\nButton.Last30Days              = Ostatnie 30 dni\nButton.Last60Days              = Ostatnie 60 dni\nButton.Last90Days              = Ostatnie 90 dni\nButton.LiabilityAccounts       = Konta zobowi\\u0105za\\u0144\nButton.Locked                  = Zablokowane\nButton.MatchAccountOnly        = Dopasuj wykorzystuj\\u0105c tylko transakcje dotycz\\u0105ce konkretnego konta\nButton.MatchAllTrans           = Dopasuj wykorzystuj\\u0105c wszystkie transakcje\nButton.MatchCaseSensitive      = Wyszukiwanie uwzgl\\u0119dnia wielko\\u015B\\u0107 liter\nButton.Modify                  = Zmie\\u0144\nButton.MonthlyBalance          = Saldo miesi\\u0119czne\nButton.New                     = Nowa\nButton.NewEmpty                = Nowy pusty\nButton.NewHist                 = Nowy historyczny\nButton.NewPayment              = Nowa p\\u0142atno\\u015B\\u0107\nButton.Next                    = Nast\\u0119pny\nButton.No                      = Nie\nButton.NoEndDate               = Bez daty ko\\u0144cowej\nButton.None                    = Brak\nButton.NotReconciled           = Nie uzgodniona\nButton.Ok                      = OK\nButton.Open                    = Otw\\u00F3rz\nButton.OpenLastOnStartup       = Przy uruchomieniu otw\\u00F3rz ostatni plik\nButton.Options                 = Opcje\\u2026\nButton.Order.LinuxOS           = Nie, Tak, [Anuluj | Zamknij | Wyczy\\u015B\\u0107], [OK | Wprowad\\u017A] (Linux)\nButton.Order.MacOS             = Nie, Tak, [Anuluj | Zamknij | Wyczy\\u015B\\u0107], [OK | Wprowad\\u017A] (Mac)\nButton.Order.WindowsOS         = Tak, Nie, [OK | Wprowad\\u017A], [Anuluj | Zamknij | Wyczy\\u015B\\u0107] (Windows)\nButton.PageSetup               = Konfiguracja strony\nButton.PlaceHolder             = Wype\\u0142niacz\nButton.Portrait                = Portrait\nButton.Print                   = Drukuj\nButton.PrintSample             = Drukuj przyk\\u0142ad\nButton.Properties              = W\\u0142a\\u015Bciwo\\u015Bci\nButton.Reconcile               = Uzgodnij\nButton.ReconcileBoth           = Wszystkie konta transakcyjne maj\\u0105 ten sam uzgodniony stan\nButton.ReconcileDisable        = Wy\\u0142\\u0105cz automatyczne uzgadnianie\nButton.ReconcileIncomeExpense  = Automatycznie uzgadniaj konta dochod\\u00F3w i koszt\\u00F3w\nButton.Reconciled              = Uzgodniona\nButton.Refresh                 = Od\\u015Bwie\\u017C\nButton.RegDate                 = Zapami\\u0119taj dat\\u0119 ostatniej transakcji\nButton.Register                = Rejestr\nButton.RegisterFollowsList     = Rejestr pod\\u0105\\u017Ca za list\\u0105 kont\nButton.RememberPassword        = Zapami\\u0119taj has\\u0142o\nButton.RemindLater             = Przypomnij mi p\\u00F3\\u017Aniej\nButton.Reminders               = Transakcje powtarzalne\nButton.RemoteServer            = Zdalny serwer\nButton.Remove                  = Usu\\u0144\nButton.RemoveOldBackups        = Usu\\u0144 stare kopie zapasowe\nButton.Rename                  = Zmie\\u0144 nazw\\u0119\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Zresetuj wszystko\nButton.Resize                  = Zmie\\u0144 rozmiar\nButton.RestoreDefault          = Przywr\\u00F3\\u0107 warto\\u015Bci domy\\u015Blne\nButton.RestoreLastTranTab      = Przywr\\u00F3\\u0107 ostatnio u\\u017Cyt\\u0105 zak\\u0142adk\\u0119 transakcji\nButton.RoundToWhole            = Zaokr\\u0105gl do pe\\u0142nych kwot\nButton.RunningBalance          = Saldo bie\\u017C\\u0105ce\nButton.Save                    = Zapisz\nButton.SaveFilters             = Zapisz filtry\nButton.SaveImage               = Zapisz obraz\nButton.Select                  = Wybierz\nButton.SelectAll               = Zaznacz wszystko\nButton.SelectText              = Wybierz tekst w fokusie\nButton.ShowCommodities         = Poka\\u017C wszystkie towary\nButton.ShowEmptyAccounts       = Poka\\u017C konta z saldem zerowym\nButton.ShowPercentValues       = Poka\\u017C warto\\u015Bci procentowe\nButton.ShowTimestamp           = Poka\\u017C godzin\\u0119\nButton.Splits                  = Podziel\nButton.Start                   = Start\nButton.Stop                    = Stop\nButton.SubstanceAnimations     = W\\u0142\\u0105cz animacj\\u0119 wygl\\u0105du i zachowania tre\\u015Bci\nButton.SumColVis               = Poka\\u017C kolumny z podsumowaniem\nButton.SumRowVis               = Poka\\u017C wiersze z podsumowaniem\nButton.ThemesEnabled           = Motywy w\\u0142\\u0105czone\nButton.Timestamp               = Znacznik czasu\nButton.Today                   = Dzisiaj\nButton.UpdateCurrenciesStartup = Aktualizuj kursy walut przy uruchomieniu\nButton.UpdateOnline            = Aktualizuj online\nButton.UpdateSecuritiesStartup = Aktualizuj papiery warto\\u015Bciowe przy uruchomieniu\nButton.UseDailyRate            = Stosuj codziennie okresowy kurs\nButton.UseEncryption           = U\\u017Cyj szyfrowanie\nButton.UseFuzzyMatch           = U\\u017Cyj rozmyte wyszukiwanie\nButton.UseLongNames            = U\\u017Cyj d\\u0142ugich nazw\nButton.UseProxy                = U\\u017Cyj proxy\nButton.UseRegexForFilter       = U\\u017Cyj wyra\\u017Ce\\u0144 regularnych dla filtr\\u00F3w\nButton.Vertical                = Pionowo\nButton.Yes                     = Tak\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Konto\nColumn.AccountName                    = Nazwa konta\nColumn.Action                         = Operacja\nColumn.Actual                         = Rzeczywisty\nColumn.Amount                         = Kwota\nColumn.Approve                        = Zatwierd\\u017A\nColumn.Balance                        = Saldo\nColumn.Budgeted                       = Bud\\u017Cet\nColumn.Charge                         = Pobranie\nColumn.Close                          = Kurs zamkn.\nColumn.Clr                            = Uzg.\nColumn.Code                           = Kod\nColumn.Commodity                      = Towar\nColumn.CostBasis                      = BK\nColumn.Credit                         = Ma\nColumn.Currency                       = Waluta\nColumn.Date                           = Data\nColumn.Day                            = Dnia\nColumn.Debit                          = Winien\nColumn.Decrease                       = Spadek\nColumn.Deposit                        = Wp\\u0142ata\nColumn.Description                    = Opis\nColumn.Due                            = Termin\nColumn.Enabled                        = Aktywna\nColumn.Entries                        = Pozycje\nColumn.Event                          = Zdarzenie\nColumn.ExchangeRate                   = Kurs wymiany\nColumn.Expense                        = Koszt\nColumn.Freq                           = Cz\\u0119stotliwo\\u015B\\u0107\nColumn.Gain                           = Zysk\nColumn.High                           = Kurs maks.\nColumn.Income                         = Doch\\u00F3d\nColumn.Increase                       = Wzrost\nColumn.Investment                     = Inwestycja\nColumn.LastPosted                     = Ostatnie wykonanie\nColumn.Loss                           = Strata\nColumn.Low                            = Kurs min.\nColumn.Memo                           = Notatka\nColumn.MktValue                       = Warto\\u015B\\u0107 rynk.\nColumn.Month                          = Miesi\\u0105c\nColumn.Num                            = Numer\nColumn.Payee                          = Beneficjent\nColumn.Payment                        = Zwrot\nColumn.Percentile                     = Percentile\nColumn.Period                         = Okres\nColumn.Price                          = Cena\nColumn.Print                          = Drukuj\nColumn.PropName                       = Nazwa w\\u0142a\\u015Bciwo\\u015Bci\nColumn.PropVal                        = Warto\\u015B\\u0107 w\\u0142a\\u015Bciwo\\u015Bci\nColumn.Quantity                       = Ilo\\u015B\\u0107\nColumn.Rebate                         = Rabat\nColumn.Receive                        = Otrzymane\nColumn.ReconciledBalance              = Saldo uzgodnione\nColumn.Remaining                      = Pozosta\\u0142o\nColumn.Script                         = Skrypt\nColumn.Security                       = Papier warto\\u015Bciowy\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = % portfela\nColumn.Short.Quantity                 = Il.\nColumn.Short.RealizedGain             = Zr zysk\nColumn.Short.RealizedGainPercentage   = % zr zysku\nColumn.Short.TotalGain                = Su zysk\nColumn.Short.TotalGainPercentage      = % su zysku\nColumn.Short.UnrealizedGain           = Nz zysk\nColumn.Short.UnrealizedGainPercentage = % nz zysku\nColumn.Spend                          = Wydane\nColumn.Timestamp                      = Znak czasu\nColumn.Total                          = Suma\nColumn.TotalCostBasis                 = Suma BK\nColumn.Type                           = Typ\nColumn.Value                          = Warto\\u015B\\u0107\nColumn.Volume                         = Wolumen\nColumn.Withdrawal                     = Wyp\\u0142ata\n\nDataStoreType.Bxds = Plik binarny\nDataStoreType.H2   = Relacyjna baza danych H2\nDataStoreType.HSQL = Relacyjna baza danych HyperSQL\nDataStoreType.XML  = Plik XML\n\nItem.Amount         = Kwota\nItem.CashDeposit    = Wp\\u0142ata got\\u00F3wkowa\nItem.CashWithdrawal = Wyp\\u0142ata got\\u00F3wkowa\nItem.Date           = Data\nItem.EFT            = EFT\nItem.Memo           = Notatka\nItem.NextNum        = Nast. nr #\nItem.Payee          = Beneficjent\nItem.Trans          = Trans\n\nLabel.AccentColor         = Kolor akcentu:\nLabel.Account             = Konto:\nLabel.AccountCode         = Kod konta:\nLabel.AccountNumber       = Nr konta:\nLabel.AccountOptions      = Opcje konta:\nLabel.AccountSeparator    = Separator konta:\nLabel.AccountStatus       = Status konta:\nLabel.AccountType         = Typ konta:\nLabel.Action              = Operacja:\nLabel.Amount              = Kwota:\nLabel.AnIntRate           = Roczna stopa procentowa (APR):\nLabel.Available           = Dost\\u0119pne:\nLabel.Balance             = Saldo:\nLabel.BankAccount         = Rachunek bankowy:\nLabel.BankID              = ID banku:\nLabel.BaseAccount         = Konto podstawowe:\nLabel.BaseColor           = Kolor bazowy:\nLabel.Bottom              = Bottom:\nLabel.By                  = Wed\\u0142ug:\nLabel.CashBalance         = Saldo got\\u00F3wki:\nLabel.Close               = Kurs zamkn.:\nLabel.Color=Color:\nLabel.Commodity           = Towar:\nLabel.CompDaysPerYear     = Dni kuponowe w roku:\nLabel.CompPerTerm         = Okresy kuponowe w roku:\nLabel.Compare             = Por\\u00F3wnaj z:\nLabel.ConfirmPassword     = Potwierd\\u017A has\\u0142o:\nLabel.ConnTimeout         = Koniec czasu po\\u0142\\u0105czenia:\nLabel.Count               = Liczba:\nLabel.CreateCurr          = Utw\\u00F3rz w\\u0142asn\\u0105 walut\\u0119:\nLabel.CssFiles            = Pliki CSS\nLabel.CsvFiles            = Pliki CSV\nLabel.Curr/Comm           = Waluta / Towar:\nLabel.Currencies          = Waluty:\nLabel.Currency            = Waluta:\nLabel.Current             = Bie\\u017Cacy:\nLabel.DatabaseBackend     = Rodzaj bazy danych:\nLabel.DatabaseName        = Lokalizacja bazy danych:\nLabel.DatabaseServer      = Serwer bazy danych:\nLabel.Date                = Data:\nLabel.DateFormat          = Format daty:\nLabel.DaysPastDue         = Dni przeterminowania:\nLabel.DefaultCurrency     = Waluta domy\\u015Blna:\nLabel.DelaySec            = Op\\u00F3\\u017Anienie (sekundy)\nLabel.Description         = Opis:\nLabel.DestAccount         = Konto docelowe:\nLabel.Difference          = R\\u00F3\\u017Cnica:\nLabel.Dividend            = Dywidenda:\nLabel.EndDate             = Data ko\\u0144ca:\nLabel.EndOn               = Do dnia:\nLabel.EndRow              = Wiersz ko\\u0144cowy:\nLabel.EndingBalance       = Saldo ko\\u0144cowe:\nLabel.EquityAccount       = Konto kapita\\u0142u:\nLabel.EscrowPmi           = Escrow, ubezp. itd.:\nLabel.Event               = Zdarzenie:\nLabel.Every               = Co:\nLabel.ExchangeAmount      = Kwota wymiany:\nLabel.ExchangeRate        = Kurs wymiany:\nLabel.ExchangedAmount     = Kwota wymieniana:\nLabel.Fees                = Op\\u0142aty:\nLabel.FeesAccount         = Konto op\\u0142at:\nLabel.FileName            = Nazwa pliku:\nLabel.FillAll             = Wype\\u0142nij wszystko:\nLabel.FirstPayDate        = Data pierwszej p\\u0142atno\\u015Bci:\nLabel.FocusColor          = Kolor fokusa:\nLabel.Format              = Format:\nLabel.Frequency           = Cz\\u0119stotliwo\\u015B\\u0107:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Zyski:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Wysoko\\u015B\\u0107:\nLabel.High                = Kurs maks.:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = CUSIP / ISIN:\nLabel.IncomeAccount       = Konto dochodu:\nLabel.InterestAccount     = Konto odsetek:\nLabel.LastOccurrence      = Ostatnie wyst\\u0105pienie:\nLabel.Layout              = Uk\\u0142ad:\nLabel.Left                = Left:\nLabel.LoanTerm            = D\\u0142ugo\\u015B\\u0107 po\\u017Cyczki (mies.):\nLabel.Low                 = Kurs min.:\nLabel.MarketValue         = Warto\\u015B\\u0107 rynkowa:\nLabel.MaxBackupCount      = Maks. liczba zachowanych kopii zapasowych:\nLabel.Memo                = Notatka:\nLabel.Monospace           = R\\u00F3wnej szeroko\\u015Bci:\nLabel.Name                = Nazwa:\nLabel.NetIncome           = Doch\\u00F3d netto:\nLabel.NewPassword         = Nowe has\\u0142o:\nLabel.NextPayDate         = Data nast\\u0119pnej p\\u0142atno\\u015Bci:\nLabel.Notes               = Notatki:\nLabel.NumTrans            = Liczba transakcji:\nLabel.Number              = Numer:\nLabel.OfxFiles            = Pliki OFX\nLabel.OpenStateDate       = Data salda pocz\\u0105tkowego:\nLabel.OpeningBalance      = Saldo pocz\\u0105tkowe:\nLabel.OrigLoanAmt         = Pocz\\u0105tkowa kwota po\\u017Cyczki:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Has\\u0142o:\nLabel.Path                = \\u015Bcie\\u017Cka\nLabel.Pattern             = Wz\\u00F3r:\nLabel.PayPerTerm          = P\\u0142atno\\u015Bci rocznie:\nLabel.Payee               = Beneficjent:\nLabel.Period              = Okres:\nLabel.Port                = Port:\nLabel.Prefix              = Prefiks:\nLabel.Price               = Cena:\nLabel.Proportional        = Proporcjonalna:\nLabel.Quantity            = Ilo\\u015B\\u0107:\nLabel.QuoteSource         = \\u0179r\\u00F3d\\u0142o notowa\\u0144:\nLabel.ReceivingAccount    = Konto odbiorcy:\nLabel.ReconciledBalance   = Saldo uzgodnione:\nLabel.RemindLater         = Przypomnij ponownie za:\nLabel.RenameBudget        = Zmie\\u0144 nazw\\u0119 bud\\u017Cetu na:\nLabel.RepeatOn            = Powt\\u00F3rz dnia:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Waluta notowa\\u0144:\nLabel.Resolution          = Rozdzielczo\\u015B\\u0107:\nLabel.ReturnOfCapital     = Zwrot kapita\\u0142u:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Dok\\u0142adno\\u015B\\u0107:\nLabel.Securities          = Papiery warto\\u015Bciowe:\nLabel.Security            = Papier warto\\u015Bciowy:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Poka\\u017C puste konta\nLabel.SortOrder           = Kolejno\\u015B\\u0107 sortowania:\nLabel.SpreadsheetFiles    = Pliki arkusza kalkulacyjnego\nLabel.StartDate           = Data pocz\\u0105tku:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Pozycja pocz\\u0105tkowa:\nLabel.StartRow            = Wiersz pocz\\u0105tkowy:\nLabel.StatementDate       = Data wyci\\u0105gu:\nLabel.StorageType         = Spos\\u00F3b przechowywania:\nLabel.Suffix              = Przyrostek:\nLabel.Symbol              = Symbol:\nLabel.TargetBalance       = Saldo docelowe:\nLabel.Top                 = Top:\nLabel.Total               = \\u0141\\u0105cznie:\nLabel.Transaction         = Transakcja:\nLabel.TransferFrom        = Przelew z:\nLabel.TransferTo          = Przelew na:\nLabel.Type                = Typ:\nLabel.Units               = Units:\nLabel.UserName            = Nazwa u\\u017Cytkownika:\nLabel.Value               = Warto\\u015B\\u0107:\nLabel.Verify              = Sprawd\\u017A:\nLabel.VerifyPassword      = Sprawd\\u017A has\\u0142o:\nLabel.Visible             = Widoczny:\nLabel.Volume              = Wolumen:\nLabel.Width               = Width:\nLabel.XMLFiles            = Pliki XML\nLabel.Year                = Rok:\nLabel.jGnashFiles         = Pliki jGnash\n\nMenu.About.Name                       = O\\u2026\nMenu.About.Tooltip                    = Informacja o jGnash\nMenu.Account.Name                     = Konto\nMenu.AccountRegister.Name             = Rejestr kont\\u2026\nMenu.AddRemoveCurrency.Name           = Dodaj / Usu\\u0144\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Aktualizuj waluty\nMenu.BackgroundCurrencyUpdate.Tooltip = Aktualizuje wszystkie kursy wymiany w tle\nMenu.BackgroundSecurityUpdate.Name    = Aktualizuj papiery warto\\u015Bciowe\nMenu.BackgroundSecurityUpdate.Tooltip = Aktualizuje wszystkie wyceny papier\\u00F3w warto\\u015Bciowych w tle\nMenu.BalanceSheet.Name                = Bilans\\u2026\nMenu.BaseColor.Name                   = Zmie\\u0144 kolor bazowy\\u2026\nMenu.BudgetManager.Name               = Zarz\\u0105dzanie bud\\u017Cetami\\u2026\nMenu.ChangeCredentials.Name           = Zmie\\u0144 has\\u0142o bazy danych\\u2026\nMenu.ChangeCredentials.Tooltip        = Zmienia has\\u0142o bezpiecznej bazy danych\nMenu.Charts.Name                      = Wykresy\nMenu.Cleared.Name                     = Rozliczone\nMenu.Close.Name                       = Zamknij\nMenu.Close.Tooltip                    = Zamyka aktywny plik\nMenu.CloseAllWindows.Name             = Zamknij wszystkie okna\nMenu.ConfigImportFilters.Name         = Konfiguruj filtry importu transakcji\\u2026\nMenu.Console.Name                     = Konsola\\u2026\nMenu.Copy.Name                        = Kopiuj\nMenu.Copy.Tooltip                     = Tworzy kopi\\u0119 wybranego elementu\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = Waluty\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Zmie\\u0144 domy\\u015Bln\\u0105\\u2026\nMenu.DefaultCurrency.Tooltip          = Zmienia walut\\u0119 domy\\u015Bln\\u0105\nMenu.Delete.Name                      = Usu\\u0144\nMenu.Duplicate.Name                   = Kopiuj\nMenu.Edit.Name                        = Edytuj\nMenu.EditTranNumList.Name             = Edytuj list\\u0119 numer\\u00F3w transakcji\\u2026\nMenu.Exit.Name                        = Opu\\u015B\\u0107\nMenu.Exit.Tooltip                     = Wyj\\u015Bcie z jGnash\nMenu.Export.Name                      = Eksport\nMenu.ExportAccounts.Name              = Eksportuj konta\\u2026\nMenu.File.Archive                     = Archiwum\\u2026\nMenu.File.Name                        = Plik\nMenu.Filter.Name                      = Filtry\nMenu.FontSize.Name                    = Zmie\\u0144 domy\\u015Bln\\u0105 wielko\\u015B\\u0107 czcionki\\u2026\nMenu.Help.Name                        = Pomoc\nMenu.Hide.Name                        = Ukryj\nMenu.HistoryChart.Name                = Wykres historyczny\\u2026\nMenu.HistoryCommodity.Name            = Historia\\u2026\nMenu.HistoryImport.Name               = Import historii\\u2026\nMenu.IEBarChart.Name                  = Wykres s\\u0142upkowy Doch\\u00F3d / Koszt\\u2026\nMenu.IEPieChart.Name                  = Wykres ko\\u0142owy Doch\\u00F3d / Koszt\\u2026\nMenu.Import.Name                      = Import\nMenu.ImportAccounts.Name              = Importuj konta\\u2026\nMenu.ImportAccounts.Tooltip           = Importuje pliki kont jGnash\nMenu.ImportJgnash.Name                = Importuj jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = Importuje pliki jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940\\u2026\nMenu.ImportMt940.Tooltip              = Importuje pliki SWIFT MT940\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Importuje pliki OFX i QFX\nMenu.ImportQif.Name                   = QIF\\u2026\nMenu.Jump.Name                        = Skocz\nMenu.ListOfAccounts.Name              = Lista kont\\u2026\nMenu.Locale.Name                      = Zmie\\u0144 ustawienia regionalne\\u2026\nMenu.LookAndFeel.Name                 = Wygl\\u0105d\nMenu.MarkAs.Name                      = Zaznacz jako\nMenu.Modify.Name                      = Modyfikuj\nMenu.ModifyCommodity.Name             = Utw\\u00F3rz / Modyfikuj\\u2026\nMenu.ModifyCurrency.Name              = Modyfikuj\\u2026\nMenu.ModifyExchangeRates.Name         = Edytuj kursy wymiany\\u2026\nMenu.MonthBalance.Name                = Saldo miesi\\u0119czne\\u2026\nMenu.MonthBalanceCompare.Name         = Por\\u00F3wnanie sald miesi\\u0119cznych\\u2026\nMenu.MonthEndBalance.Name             = Saldo na koniec miesi\\u0105ca\\u2026\nMenu.MonthEndBalanceCSV.Name          = Saldo na koniec miesi\\u0105ca (CSV)\\u2026\nMenu.NetWorth.Name                    = Warto\\u015B\\u0107 netto\\u2026\nMenu.New.Name                         = Nowy\\u2026\nMenu.New.Tooltip                      = Tworzy nowy plik\nMenu.NewReminder.Name                 = Utw\\u00F3rz now\\u0105 transakcj\\u0119 powtarzaln\\u0105\nMenu.Open.Name                        = Otw\\u00F3rz\\u2026\nMenu.Open.Tooltip                     = Otwiera wybrany plik\nMenu.Option.Name                      = Opcje\\u2026\nMenu.Option.Tooltip                   = Zmienia opcje programu\nMenu.PackDatabase.Name                = Pakuj baz\\u0119 danych\\u2026\nMenu.PayeePieChart.Name               = Wykres ko\\u0142owy Doch\\u00F3d / Koszt wg beneficjent\\u00F3w\\u2026\nMenu.PeriodicAccountBalance.Name      = Okresowe saldo konta\\u2026\nMenu.Portfolio.Name                   = Portfel\\u2026\nMenu.ProfitLoss.Name                  = Zysk i strata\\u2026\nMenu.ProfitLossTXT.Name               = Zysk i strata (tekst)\\u2026\nMenu.Reconcile.Name                   = Uzgodnij\nMenu.Reconciled.Name                  = Uzgodnione\nMenu.RecurringList.Name               = Transakcje powtarzalne\\u2026\nMenu.Register.Name                    = _Rejestr\\u2026\nMenu.Reports.Name                     = Raporty\nMenu.RunJavaScript.Name               = Uruchom JavaScript\\u2026\nMenu.RunJavaScript.Tooltip            = Uruchamia JavaScript\nMenu.Save.Name                        = Zapi_sz\nMenu.Save.Tooltip                     = Zapisuje aktualny plik\nMenu.SaveAs.Name                      = Z_apisz jako\\u2026\nMenu.SaveAs.Tooltip                   = Zapisuje aktualny plik pod now\\u0105 nazw\\u0105\nMenu.Securities.Name                  = Papiery warto\\u015Bciowe\nMenu.SetPassword.Name                 = Ustaw has\\u0142o\\u2026\nMenu.Show.Name                        = Poka\\u017C\nMenu.ShutdownServer.Name              = Wy\\u0142\\u0105cz serwer\\u2026\nMenu.ShutdownServer.Tooltip           = Wy\\u0142\\u0105cza serwer i zapobiega dalszej komunikacji\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Motywy\nMenu.Tools.Name                       = Narz\\u0119dzia\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = Nie uzgodnione\nMenu.View.Name                        = Widok\nMenu.Window.Name                      = Okno\n\nMessage.AcceptLicense                = Przeczyta\\u0142em, rozumiem i akceptuj\\u0119 warunki licencji.\nMessage.AccountAdd                   = Konto dodane\nMessage.AccountCode                  = Kod konta nie by\\u0142 unikalny: Kod nie zosta\\u0142 zmieniony\nMessage.AccountLocked                = Konto jest zablokowane\nMessage.AccountModify                = Konto zmodyfikowane\nMessage.AccountMoveFailed            = Nie mo\\u017Cna przenie\\u015B\\u0107 konta\nMessage.AccountRemove                = Konto usuni\\u0119te\nMessage.AntiAlias                    = W\\u0142\\u0105czone wyg\\u0142adzanie tekstu!\nMessage.AutoSaveOff                  = Automatyczne zapisywanie zosta\\u0142o wy\\u0142\\u0105czone\nMessage.AutoSaveOn                   = Automatyczne zapisywanie zosta\\u0142o w\\u0142\\u0105czone\nMessage.CSVFile                      = Pliki rozdzielane przecinkami (*.csv)\nMessage.CheckRecurring               = Sprawdzanie nowych zdarze\\u0144 powtarzalnych\nMessage.ClosingFile                  = Zamykanie pliku\nMessage.CollectingReportData         = Gromadzenie danych raportu\nMessage.CompilingReport              = Kompilacja raportu\nMessage.Confirm.ExecuteReminder      = Wykona\\u0107 teraz transakcj\\u0119 powtarzaln\\u0105?\nMessage.ConfirmBudgetDelete          = Usun\\u0105\\u0107 wybrany bud\\u017Cet?\nMessage.ConfirmMultipleBudgetDelete  = Usun\\u0105\\u0107 wybrane bud\\u017Cety?\nMessage.ConfirmMultipleTransDelete   = Usun\\u0105\\u0107 wybrane transakcje?\nMessage.ConfirmReminderDelete        = Usun\\u0105\\u0107 wybran\\u0105 transakcj\\u0119 powtarzaln\\u0105?\nMessage.ConfirmSecurityHistoryDelete = Usun\\u0105\\u0107 wybran\\u0105 histori\\u0119 papier\\u00F3w warto\\u015Bciowych?\\n\\nUsuwanie historii nast\\u0105pi w tle i mo\\u017Ce chwil\\u0119 potrwa\\u0107.\nMessage.ConfirmTransDelete           = Usun\\u0105\\u0107 wybran\\u0105 transakcj\\u0119?\nMessage.CredentialChange             = Zmiana po\\u015Bwiadczenia by\\u0142a udana\nMessage.CurrChange                   = Waluta domy\\u015Blna zmieniona na:\nMessage.DownloadingX                 = \\u015Aci\\u0105ganie {0}\nMessage.EngineStart                  = Silnik wystartowa\\u0142\nMessage.EnterNetworkAuth             = Wprawd\\u017A uwierzytelnienie sieciowe\nMessage.Error.AccountCreate          = Nie mo\\u015Cna utworzy\\u0107 konta\nMessage.Error.AccountRemove          = Nie mo\\u015Cna usun\\u0105\\u0107 konta\nMessage.Error.AccountUpdate          = W czasie aktualizacji konta wyst\\u0105pi\\u0142 b\\u0142\\u0105d\nMessage.Error.AddCommodity           = Nie mo\\u015Cna doda\\u0107 towaru\nMessage.Error.AddCurrency            = Nie mo\\u015Cna doda\\u0107 waluty\nMessage.Error.AmortizationSave       = Nie uda\\u0142o si\\u0119 zapisywanie ustawie\\u0144 amortyzacji\nMessage.Error.BudgetDuplicate        = Nie uda\\u0142o si\\u0119 skopiowanie bud\\u017Cetu\nMessage.Error.BudgetRemove           = Nie uda\\u0142o si\\u0119 usuwanie bud\\u017Cetu\nMessage.Error.CreateBasicAccounts    = Utw\\u00F3rz najpierw bazowy zestaw kont\nMessage.Error.CredentialChange       = Nie uda\\u0142a si\\u0119 zmiana po\\u015Bwiadczenia\nMessage.Error.CreditDebit.Equal      = Konta Winien i Ma mog\\u0105 by\\u0107 r\\u00F3wne\nMessage.Error.CurrencyUpdate         = Nie mo\\u015Cna zaktualizowa\\u0107 waluty {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Nie mo\\u015Cna usun\\u0105\\u0107 za\\u0142\\u0105cznik {0}\nMessage.Error.DeleteExistingFile     = Nie mo\\u015Cna usun\\u0105\\u0107 istniej\\u0105cy pliku {0}\nMessage.Error.Duplicate              = Kopie s\\u0105 niedozwolone\nMessage.Error.EmptyKey               = Klucz atrybutu nie mo\\u017Ce by\\u0107 pusty lub null\nMessage.Error.FileNotFound           = Nie znaleziono pliku\nMessage.Error.HistRemoval            = Nie mo\\u015Cna usun\\u0105\\u0107 w\\u0119z\\u0142a historii {0,date,MM/dd/yyyy} z {1}\nMessage.Error.IOError                = B\\u0142\\u0105d We/Wy\nMessage.Error.InvalidAccountGroup    = B\\u0142\\u0119dna grupa kont\nMessage.Error.InvalidTransactionTag  = Nieprawid\\u0142owy znacznik transakcji\nMessage.Error.InvalidTransactionType = Nieprawid\\u0142owy typ transakcji\nMessage.Error.InvalidUserPass        = Nieprawid\\u0142owe has\\u0142o lub pr\\u00F3ba otworzenia b\\u0142\\u0119dnego typu pliku\nMessage.Error.License                = Licencja nie zosta\\u0142a zaakceptowana\nMessage.Error.LoadingFile            = Podczas \\u0142adowania pliku wyst\\u0105pi\\u0142 b\\u0142\\u0105d\nMessage.Error.LogFileHandler         = Nie mo\\u017Cna zainstalowa\\u0107 obs\\u0142ugi pliku dziennika\nMessage.Error.MissingAttachment      = Nie mo\\u015Cna znale\\u017A\\u0107 za\\u0142\\u0105cznika \"{0}\"\nMessage.Error.ModifyCommodity        = Nie mo\\u015Cna zmodyfikowa\\u0107 towaru\nMessage.Error.ModifyCurrency         = Nie mo\\u015Cna zmodyfikowa\\u0107 waluty\nMessage.Error.MoveAccount            = Nie mo\\u015Cna przesun\\u0105\\u0107 konta\nMessage.Error.NewBudget              = Nie uda\\u0142o si\\u0119 utworzenie nowego bud\\u017Cetu\nMessage.Error.ParseTransactions      = Nie przetworzono \\u017Cadnych transakcji\nMessage.Error.PasswordMatch          = Has\\u0142a nie pasuj\\u0105 do siebie\nMessage.Error.ReminderAdd            = Nie uda\\u0142o si\\u0119 dodawanie transakcji powtarzalnej\nMessage.Error.ReminderUpdate         = Nie uda\\u0142o si\\u0119 zaktualizowanie transakcji powtarzalnej\nMessage.Error.RemoveTempFile         = Nie mog\\u0119 usun\\u0105\\u0107 plik\\u00F3w tymczasowych\nMessage.Error.SecurityAccountRemove  = Nie uda\\u0142o si\\u0119 usuwanie papieru warto\\u015Bciowego {0} z konta {1}\nMessage.Error.SecurityAccountUpdate  = Nie mo\\u017Cna zaktualizowa\\u0107 papier\\u00F3w warto\\u015Bciowych konta\nMessage.Error.SecurityAdd            = Nie uda\\u0142o si\\u0119 dodawanie papieru warto\\u015Bciowego {0}\nMessage.Error.SecurityUpdate         = Nie mo\\u017Cna zaktualizowa\\u0107 papieru warto\\u015Bciowego {0}\nMessage.Error.ServerConnection       = Po\\u0142\\u0105czenie z serwerem nie powiod\\u0142o si\\u0119\nMessage.Error.TranAddFail            = Podczas dodawania transakcji wyst\\u0119pi\\u0142 b\\u0142\\u0119d wewn\\u0119trzny\nMessage.Error.TransferAttachment     = Za\\u0142\\u0105cznik \"{0}\" nie mo\\u017Ce by\\u0107 przeniesiony\nMessage.Error.UnsupportedFileType    = Nieobs\\u0142ugiwany typ pliku\nMessage.FileClosed                   = Plik zamkni\\u0119ty\nMessage.FileIsLocked                 = Plik jest zablokowany przez inn\\u0105 aplikacj\\u0119\nMessage.FileLoadComplete             = \\u0141adowanie pliku zako\\u0144czone\nMessage.FileNotValid                 = Wybrany plik nie jest wa\\u017Cny\nMessage.FileSaveComplete             = Zapisywanie pliku zako\\u0144czone\nMessage.ImportWait                   = Prosz\\u0119 czeka\\u0107, import mo\\u017C chwil\\u0119 trwa\\u0107\nMessage.Info.LongUpgrade             = Tw\\u00F3j plik zostanie uaktualniony do najnowszego formatu. Mo\\u017Ce to chwil\\u0119 potrwa\\u0107.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Tw\\u00F3j plik zosta\\u0142 uaktualniony do ostatniego formatu. \\nOryginalny plik zosta\\u0142 zapami\\u0119tany jako \"{0}\".\nMessage.JVM11                        = jGnash wymaga Java 11 lub nowszej\nMessage.LoadReportFail               = Nie mo\\u015Cna za\\u0142adowa\\u0107 definicji raportu\nMessage.LoadingFile                  = \\u0141aduj\\u0119 plik\\u2026\nMessage.LocaleChange                 = Domy\\u015Blne ustawienia regionalne zmienione na:\nMessage.NewVersion                   = Nowsza wersja jGnash jest dost\\u0119pna do pobrania.\nMessage.NoRepeat                     = Nie powtarzaj\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = Istniej\\u0105ca baza danych zostanie nadpisana\nMessage.PackingFile                  = Pakowanie pliku bazy danych\nMessage.PackingFileComplete          = Pakowanie pliku zako\\u0144czone\nMessage.ParseReportFail              = Nie uda\\u0142o si\\u0119 przetworzenie definicji raportu\nMessage.PleaseWait                   = Prosz\\u0119 czeka\\u0107\nMessage.PrefFail                     = Nie znaleziono starych ustawie\\u0144\nMessage.PrepShutdown                 = Przygotowanie do wy\\u0142\\u0105czenia\nMessage.ProcessingReportData         = Przetwarzanie danych raportu\nMessage.Proxy                        = Ustawianie http proxy:\nMessage.ReduceFont                   = Spr\\u00F3buj zmiejszy\\u0107 rozmiar czcionki. \\nZajrzyj do pomocnika raport\\u00F3w, aby zapozna\\u0107 si\\u0119 ze szczeg\\u00F3\\u0142ami.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Modu\\u0142y raportu zosta\\u0142y pomy\\u015Blnie za\\u0142adowane\nMessage.ReportWait                   = Prosz\\u0119 czeka\\u0107, \\u0142adowanie modu\\u0142\\u00F3w raportu\nMessage.RestartLocale                = Zrestartuj, aby zmiana ustawie\\u0144 regionalnych odnios\\u0142a skutek\nMessage.SavingFile                   = Zapisywanie pliku\\u2026\nMessage.SearchWait                   = Wyszukiwanie, prosz\\u0119 czeka\\u0107\nMessage.Shutdown                     = Wy\\u0142\\u0105cz\nMessage.StartEndDate                 = Wprowad\\u017A dat\\u0119 pocz\\u0105tku i ko\\u0144ca\nMessage.StoreBackup                  = Zapisywanie kopii zapasowej do:\nMessage.StoreComplete                = Zapisywanie pliku zako\\u0144czone\nMessage.StoreWait                    = Oczekiwanie na zako\\u0144czenie zapisywania pliku\nMessage.TXTFile                      = Pliki tekstowe (*.txt)\nMessage.TransactionAccountLocked     = Nie uda\\u0142o si\\u0119 dodawanie transakcji, konta docelowe s\\u0105 zablokowane\nMessage.TransactionAdd               = Transakcja dodana\nMessage.TransactionModifyLocked      = Transakcja nie mo\\u017Ce by\\u0107 zmodyfikowana. \\nKonto docelowe jest zablokowane przed modyfikacj\\u0105.\nMessage.TransactionRemove            = Transakcja usuni\\u0119ta\nMessage.TransactionRemoveLocked      = Nie uda\\u0142o si\\u0119 usuwanie transakcji, konta docelowe s\\u0105 zablokowane\nMessage.UninstallBad                 = Nie uda\\u0142o si\\u0119 pomy\\u015Blne odinstalowanie\nMessage.UninstallGood                = Odinstalowanie pomy\\u015Blne!\nMessage.UpdatedPrice                 = Zaktualizowano cen\\u0119 dla {0}\nMessage.UpdatedPriceDate             = Zaktualizowano cen\\u0119 dla {0}, {1}\nMessage.UpdatedSecurityEvent         = Zaktualizowano wydarzenie korporacyjne dla {0}\nMessage.Version                      = U\\u017Cywasz wersji\nMessage.Warn.CommodityInUse          = Towar jest u\\u017Cywany\nMessage.Warn.ConfigAmortization      = Skonfiguruj, prosz\\u0119, amortyzacj\\u0119\nMessage.Warn.CurrencyInUse           = Waluta jest jest u\\u017Cywana\nMessage.Warn.FailedTransInfoRemoval  = Nie uda\\u0142o si\\u0119 usuni\\u0119cie starej informacji o transakcjach\nMessage.Warn.MoveFile                = Plik \"{0}\" zostanie przesuni\\u0119ty \\ndo zarz\\u0105dzanej lokalizacji \"{1}\". \\n\\nCzy kontynuowa\\u0107?\nMessage.Warn.SameFile                = Plik \"{0}\" ju\\u017C istnieje \\nw zarz\\u0105dzanej lokalizacji \"{1}\". \\n\\nPlik nie zostanie przesuni\\u0119ty.\nMessage.Warn.WindowWidth             = Okno jest zbyt ma\\u0142e.\\n\\nPowi\\u0119ksz szeroko\\u015B\\u0107 lub zredukuj wielko\\u015B\\u0107 czcionki.\n\nName.BankAccounts    = Konta bankowe\nName.ExpenseAccounts = Konta koszt\\u00F3w\nName.IncomeAccounts  = Konta dochod\\u00F3w\nName.Root            = Korze\\u0144\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = Od {0,date,long} do {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = Strona {0} z {1}\nPattern.QuarterOfYear  = Kwarta\\u0142 {0,number,#} z {1,number,#}\nPattern.WeekOfYear     = Tydzie\\u0144 {0,number,00} z {1,number,#}\n\nPeriod.10Min     = 10 minut\nPeriod.15Min     = 15 minut\nPeriod.1Day      = 1 dzie\\u0144\nPeriod.1Hr       = 1 godzina\nPeriod.2Hr       = 2 godziny\nPeriod.30Min     = 30 minut\nPeriod.5Min      = 5 minut\nPeriod.8Hr       = 8 godzin\nPeriod.BiWeekly  = dwa tygodnie\nPeriod.Daily     = dzie\\u0144\nPeriod.Monthly   = miesi\\u0105c\nPeriod.NextStart = Przy nast\\u0119pnym uruchomieniu jGnash\nPeriod.None      = Brak\nPeriod.OnlyOnce  = Tylko raz\nPeriod.Quarterly = kwarta\\u0142\nPeriod.Weekly    = tydzie\\u0144\nPeriod.Yearly    = rok\n\nQuestion.DeleteAttachment = Ta transakcja ma za\\u0142\\u0105cznik. \\n\\nCzy chcesz usun\\u0105\\u0107 r\\u00F3wnie\\u017C za\\u0142\\u0105cznik?\n\nQuoteSource.None     = Brak\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dywidenda\nSecurityEvent.Price    = Cena\nSecurityEvent.Split    = Podzia\\u0142\n\nSequence.EveryFifthRow  = Co pi\\u0105ty wiersz\nSequence.EveryForthRow  = Co czwarty wiersz\nSequence.EveryOtherRow  = Dla innego wiersza\nSequence.EveryRow       = Dla ka\\u017Cdego wiersza\nSequence.EverySecondRow = Co drugi wiersz\nSequence.EveryThirdRow  = Co trzeci wiersz\n\nSortOrder.AccountBalanceDesc = Saldo konta\nSortOrder.AccountName        = Nazwa konta\n\nState.Cleared       = R\nState.NotReconciled = \\u200B\nState.Reconciled    = U\n\nTab.About           = O\nTab.Accounts        = Konta\nTab.Adjust          = Dostosuj\nTab.AppLicense      = Licencja jGnash\nTab.Budgeting       = Bud\\u017Cetowanie\nTab.Charge          = Do\\u0142adowanie\nTab.Credit          = Uznanie\nTab.Credits         = Uznania\nTab.DataEngine      = Silnik danych\nTab.DataProviders   = Data Providers\nTab.Day             = Dzie\\u0144\nTab.Debit           = Obci\\u0105\\u017Cenie\nTab.Formats         = Formats\nTab.GPLLicense      = Licencja GPL\nTab.General         = Og\\u00F3lne\nTab.LGPLLicense     = Licencja LGPL\nTab.License         = Licencja\nTab.Month           = Miesi\\u0105c\nTab.Network         = Sie\\u0107\nTab.None            = Brak\nTab.Payment         = P\\u0142atno\\u015B\\u0107\nTab.Register        = Rejestr\nTab.Reminders       = Powtarzalne\nTab.Report          = Raport\nTab.StartupShutdown = Uruchomienie / Wy\\u0142\\u0105czenie\nTab.SysInfo         = Informacja systemowa\nTab.Transfer        = Przelew\nTab.Week            = Tydzie\\u0144\nTab.Year            = Rok\n\nTag.Bank                   = Bank\nTag.Dividend               = Dywidenda\nTag.FeesOffset             = Op\\u0142aty\nTag.GainLoss               = Zyski / (Strata)\nTag.GainsOffset            = Zyski\nTag.Investment             = Inwestycja\nTag.InvestmentCashTransfer = Przelew got\\u00F3wki z inwestycji\nTag.InvestmentFee          = Op\\u0142ata inwestycyjna\nTag.LTCG                   = D\\u0142ugoterminowy rozk\\u0142ad zysk\\u00F3w kapita\\u0142owych\nTag.MTCG                   = \\u015Arednioterminowy rozk\\u0142ad zysk\\u00F3w kapita\\u0142owych\nTag.Misc                   = R\\u00F3\\u017Cne\nTag.NonTaxableInterest     = Odsetki niepodlegaj\\u0105ce opodatkowaniu\nTag.STCG                   = Kr\\u00F3tkoterminowy rozk\\u0142ad zysk\\u00F3w kapita\\u0142owych\nTag.TaxableInterest        = Odsetki podlegaj\\u0105ce opodatkowaniu\nTag.Vat                    = VAT\n\nTitle.About                      = O\nTitle.AccountBalance             = Saldo konta\nTitle.AccountFilter              = Filtry konta\nTitle.AccountGroups              = Grupy kont\nTitle.AccountInfo                = Informacja o koncie\nTitle.AccountRegister            = Rejestr kont\nTitle.AccountSecurities          = Papiery warto\\u015Bciowe konta\nTitle.AddRemCurr                 = Dodaj / Usu\\u0144 walut\\u0119\nTitle.AmortizationSetup          = Ustawienia amortyzacji\nTitle.Archive                    = Archiwum\nTitle.AutoComplete               = Autouzupe\\u0142nianie\nTitle.AutoSave                   = Automatyczne zapisywanie\nTitle.Available                  = Dost\\u0119pne\nTitle.BackgroundUpdate           = Aktualizacja w tle\nTitle.BackingStore               = Magazyn kopi\nTitle.BalanceSheet               = Bilans\nTitle.BaseColor                  = Zmie\\u0144 kolor bazowy\nTitle.BudgetGoal                 = Zarz\\u0105dzanie bud\\u017Cetuami2026\nTitle.BudgetManager              = Zarz\\u0105dzanie bud\\u017Cetami\\u2026\nTitle.BudgetProperties           = W\\u0142a\\u015Bciwo\\u015Bci bud\\u017Cetu\nTitle.ButtonOrder                = Kolejno\\u015B\\u0107 przycisk\\u00F3w\nTitle.ChangePassword             = Zmie\\u0144 has\\u0142o\nTitle.CheckDesign                = Edytor czek\\u00F3w\nTitle.ChooseAccounts             = Wyb\\u00F3r kont do utworzenia\nTitle.ColVis                     = Widoczno\\u015B\\u0107 kolumn\nTitle.Colors                     = Kolory\nTitle.CommoditiesSecurities      = Papiery warto\\u015Bciowe\nTitle.ConfigTransImportFilters   = Konfiguruj filtry importu transakcji\nTitle.Confirm                    = Potwierd\\u017A\nTitle.ConnectServer              = Po\\u0142\\u0105cz z serwerem\nTitle.Connection                 = Po\\u0142\\u0105czenie\nTitle.Console                    = Konsola\nTitle.CreateModifyCommodities    = Utw\\u00F3rz / Modyfikuj papiery warto\\u015Bciowe\nTitle.Credits                    = Uznania\nTitle.Currencies                 = Waluty\nTitle.Current                    = Bie\\u017C\\u0105ce\nTitle.CutOffDate                 = Wybierz dat\\u0119 zako\\u0144czenia\nTitle.DatabaseCfg                = Konfiguracja bazy danych\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = Obc\\u0105\\u017Cenia\nTitle.DefDefCurr                 = Definiowanie waluty domy\\u015Blnej\nTitle.DefTranNum                 = Domy\\u015Blne numery transakcji\nTitle.DefaultBehavior            = Domy\\u015Blne zachowanie\nTitle.Defaults                   = Domy\\u015Blne\nTitle.DeleteAttachment           = Usu\\u0144 za\\u0142\\u0105cznik\nTitle.Display                    = Poka\\u017C\nTitle.DuplicateTransaction       = Kopiuj transakcj\\u0119\nTitle.DuplicateTransactionsFound = Kopiuj znalezione transakcje\nTitle.EditExchangeRates          = Edytuj kursy wymiany\nTitle.EndMonthBalance            = Saldo konta na koniec miesi\\u0105ca\nTitle.EnterPassword              = Wprowad\\u017A has\\u0142o\nTitle.Entry                      = Wpis\nTitle.Error                      = B\\u0142\\u0105d\nTitle.EventHistory               = Historia zdarze\\u0144\nTitle.ExchangeRate               = Kurs wymiany\nTitle.FileImport                 = Wybierz plik do importu\nTitle.FileLoginCredentials       = Uwierzytelnienie pliku / logowania\nTitle.Filters                    = Filtry\nTitle.FontSize                   = Zmie\\u0144 domy\\u015Bln\\u0105 wielko\\u015B\\u0107 czcionki\nTitle.Fonts                      = Domy\\u015Blne czcionki\nTitle.Frequency                  = Cz\\u0119stotliwo\\u015B\\u0107\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = Pomocnik jGnash\nTitle.HistoryImport              = Import danych historycznych\nTitle.ImageFiles                 = Pliki obraz\\u00F3w\nTitle.ImpPartQif                 = Importuj cz\\u0119\\u015Bciowy plik QIF\nTitle.ImpSum                     = Importuj podsumowanie\nTitle.ImportOFX                  = Importuj plik OFX\nTitle.ImportTransactions         = Importuj transakcje z pliku\nTitle.IncomeExpenseBarChart      = Wykres s\\u0142upkowy dochod\\u00F3w i koszt\\u00F3w\nTitle.IncomeExpenseChart         = Wykres ko\\u0142owy dochod\\u00F3w i koszt\\u00F3w\nTitle.Information                = Informacje\nTitle.InvFees                    = Op\\u0142aty inwestycyjne\nTitle.InvGainsLoss               = Zysk / Strata z inwestycji\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modyfikuj transakcje\nTitle.ModOFXTrans                = Modyfikuj transakcje OFX\nTitle.ModQIFTrans                = Modyfikuj transakcje QIF\nTitle.ModifyAccount              = Modyfikuj konto\nTitle.ModifyCurrencies           = Modyfikuj waluty\nTitle.ModifyReminder             = Modyfikuj transakcj\\u0119 powtarzaln\\u0105\nTitle.ModifySecHistory           = Modyfikuj histori\\u0119 papier\\u00F3w warto\\u015Bciowych\nTitle.ModifyTransaction          = Modyfikuj transakcj\\u0119\nTitle.MoveFile                   = Przesu\\u0144 plik\nTitle.NewAccount                 = Nowe konto\nTitle.NewBudget                  = Nowy bud\\u017Cet\nTitle.NewFile                    = Utw\\u00F3rz nowy plik\nTitle.NewPassword                = Nowe has\\u0142o\nTitle.NewReminder                = Nowa transakcja powtarzalna\nTitle.NewTrans                   = Nowa transakcja\nTitle.Notes                      = Notatki\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Otw\\u00F3rz\nTitle.Options                    = Opcje\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pakowanie bazy danych\nTitle.PageSetup                  = Konfiguracja strony\nTitle.ParentAccount              = Konto nadrz\\u0119dne\nTitle.PercentDist                = Rozk\\u0142ad procentowy\nTitle.PercentExpense             = Procent koszt\\u00F3w\nTitle.PercentIncome              = Procent dochod\\u00F3w\nTitle.PleaseWait                 = Prosz\\u0119 czeka\\u0107\nTitle.PortfolioReport            = Raport z portfela\nTitle.PriceHistory               = Historia cen\nTitle.ProfitLoss                 = Rachunek zysk\\u00F3w i strat\nTitle.ReconcileSettings          = Ustawienia uzgadniania\nTitle.Reminder                   = Transakcja powtarzalna\nTitle.Reminders                  = Transakcje powtarzalne\nTitle.RenameBudget               = Zmie\\u0144 nazw\\u0119 bud\\u017Cetu\nTitle.ReportOptions              = Opcje raport\\u00F3w\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Zyski zatrzymane\nTitle.ReverseAccountBalances     = Odwr\\u00F3\\u0107 wy\\u015Bwietlane salda kont\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Zapisz jako\nTitle.SaveFile                   = Zapisz plik\nTitle.SecurityHistory            = Historia papieru warto\\u015Bciowego\nTitle.SelAccount                 = Wybierz konto\nTitle.SelAvailCurr               = Wyb\\u00F3r dost\\u0119pnych walut\nTitle.SelDate                    = Wybierz dat\\u0119\nTitle.SelDefCurr                 = Wybierz walut\\u0119 domy\\u015Bln\\u0105\nTitle.SelDefLocale               = Wybierz domy\\u015Blne ustawienia regionalne\nTitle.SelDestAccount             = Wybierz konto docelowe\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Wybierz konto kapita\\u0142u\nTitle.SelFile                    = Wybierz plik\nTitle.SelQifDateFormat           = Wybierz format daty QIF\nTitle.SelectColor                = Wybierz kolor\nTitle.Selected                   = Wybrane\nTitle.Shutdown                   = Wy\\u0142\\u0105czenie\nTitle.SmartFill                  = Inteligentne wype\\u0142nianie\nTitle.SpitTran                   = Podzia\\u0142 transakcji\nTitle.Startup                    = Uruchomienie\nTitle.Steps                      = Kroki\nTitle.Success                    = Sukces\nTitle.Summary                    = Podsumowanie\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Warunki\nTitle.Transaction                = Transakcja\nTitle.TransactionImport          = Import transakcji\nTitle.TransactionList            = Lista transakcji\nTitle.TransactionSetup           = Ustawienia transakcji\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Nieprzechwycony wyj\\u0105tek\nTitle.ViewImage                  = Poka\\u017C obraz\nTitle.Visible                    = Widoczne\nTitle.VisibleAccountTypes        = Widoczne typy kont\nTitle.Warning                    = Ostrze\\u017Cenie\n\nToolTip.AccountList                          = Lista kont\nToolTip.AccountRegister                      = Rejestr kont\nToolTip.AddAttachment                        = Dodaje za\\u0142\\u0105cznik\nToolTip.BudgetMgr                            = Tworzy, usuwa i modyfikuje bud\\u017Cety\nToolTip.Budgeting                            = Widok bud\\u017Cetowania\nToolTip.ColumnVis                            = Zmienia widoczno\\u015B\\u0107 kolumn\nToolTip.ConvertSEntry                        = Zmienia transakcj\\u0119 na transakcj\\u0119 dwustronn\\u0105\nToolTip.DeleteAccount                        = Usuwa konto\nToolTip.DeleteAllExceptFridaySecurityHistory = Usuwa ca\\u0142\\u0105 histori\\u0119 papier\\u00F3w warto\\u015Bciowych za wyj\\u0105tkiem pi\\u0105tk\\u00F3w\nToolTip.DeleteAllExceptMondaySecurityHistory = Usuwa ca\\u0142\\u0105 histori\\u0119 papier\\u00F3w warto\\u015Bciowych za wyj\\u0105tkiem poniedzia\\u0142k\\u00F3w\nToolTip.DeleteAttachment                     = Usuwa za\\u0142\\u0105cznik\nToolTip.DeleteWeekendSecurityHistory         = Usuwa histori\\u0119 papier\\u00F3w warto\\u015Bciowych wyst\\u0119puj\\u0105c\\u0105 w soboty i niedziele\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Eksportuje wybrane transakcje do pliku CSV lub OFX\nToolTip.FilterAccount                        = Filtruje konta\nToolTip.FilterAccounts                       = Filtruje wg typu konta\nToolTip.FilterMemo                           = Filtruje wg notatki\nToolTip.FilterPayee                          = Filtruje wg beneficjenta\nToolTip.FilterReconciledState                = Filtruje wg statusu uzgodnienia\nToolTip.FilterTransactionAge                 = Filtruje wg wieku transakcji\nToolTip.FontSize                             = Zmienia wielko\\u015B\\u0107 czcionki\nToolTip.FuzzyMatch                           = Dopasowanie jest oparte na podstawie ostatniego podobnego zapisu\nToolTip.Help                                 = Pomocnik\nToolTip.ISIN                                 = International Securities Identifying Number\nToolTip.IntegersOnly                         = Dozwolone jedynie warto\\u015Bci ca\\u0142kowite\nToolTip.ModifyAccount                        = Modyfikuje konto\nToolTip.NewAccount                           = Tworzy nowe konto\nToolTip.PageSetup                            = Ustawienia strony\nToolTip.PrintRegRep                          = Drukuje raport z rejestru\nToolTip.ReconcileAccount                     = Uzgadnia konto\nToolTip.Reminders                            = Transakcje powtarzalne\nToolTip.ResizeColumns                        = Zmienia wielko\\u015B\\u0107 kolumn\nToolTip.ReversedCredit                       = Odwraca znak kont po\\u017Cyczek, zobowi\\u0105za\\u0144, kapita\\u0142u i dochod\\u00F3w\nToolTip.Scale                                = Liczba cyfr z prawej strony po przecinku\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Pokazuje szczeg\\u00F3\\u0142y\nToolTip.Timestamp                            = Tworzy kopi\\u0119 zapasow\\u0105 ze znacznikiem czasu podczas zamykania\nToolTip.ViewAttachment                       = Pokazuje za\\u0142\\u0105cznik\nToolTip.ZoomRegister                         = Otwiera nowy rejestr kont\n\nTransaction.AddShare        = Dodanie akcji (Dostosowanie)\nTransaction.BuyShare        = Zakup akcji\nTransaction.Dividend        = Dywidenda\nTransaction.DoubleEntry     = Zapis dwustronny\nTransaction.MergeShare      = Scalenie akcji\nTransaction.ReinvestDiv     = Reinwestycja dywidendy\nTransaction.RemoveShare     = Usuni\\u0119cie akcji (Dostosowanie)\nTransaction.ReturnOfCapital = Zwrot kapita\\u0142u\nTransaction.SellShare       = Sprzeda\\u017C akcji\nTransaction.SingleEntry     = Zapis jednostronny\nTransaction.Split           = Transakcja podzia\\u0142u\nTransaction.SplitEntry      = Zapis transakcji podzia\\u0142u\nTransaction.SplitShare      = Podzia\\u0142 akcji\nTransaction.TransferIn      = Przelew got\\u00F3wkowy przychodz\\u0105cy\nTransaction.TransferOut     = Przelew got\\u00F3wkowy wychodz\\u0105cy\n\nWord.Add             = Dodaj\nWord.All             = Wszystkie\nWord.Balance         = Saldo\nWord.Buy             = Kup\nWord.Commodity       = Towar\nWord.Copy            = Kopia\nWord.Description     = Opis\nWord.Difference      = R\\u00F3\\u017Cnica\nWord.Dividend        = Dywidenda\nWord.Exchange        = Wymiana\nWord.Fees            = Op\\u0142aty\nWord.GrossExpense    = Koszt brutto\nWord.GrossIncome     = Doch\\u00F3d brutto\nWord.Interest        = Odsetki\nWord.Into            = na\nWord.Invalid         = B\\u0142\\u0119dny\nWord.Merge           = Po\\u0142\\u0105cz\nWord.Monthly         = Miesi\\u0119cznie\nWord.Name            = Nazwa\nWord.NetIncome       = Doch\\u00F3d netto\nWord.NetWorth        = Warto\\u015B\\u0107 netto\nWord.NewBudget       = Nowy bud\\u017Cet\nWord.None            = Brak\nWord.Quarterly       = Kwartalnie\nWord.ReInvDiv        = Reinwestuj dywidend\\u0119\nWord.Remove          = Usu\\u0144\nWord.ReturnOfCapital = Zwrot kapita\\u0142u\nWord.Seconds         = sekund(y)\nWord.Security        = Papier warto\\u015Bciowy\nWord.Sell            = Sprzedaj\nWord.Split           = Podzia\\u0142\nWord.Subtotal        = Podsuma\nWord.Total           = Suma\nWord.Totals          = Sumy\nWord.Yearly          = Rocznie\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_pt.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = Ativo\nAccountType.Bank             = Banco\nAccountType.Cash             = Dinheiro\nAccountType.Checking         = Cheques\nAccountType.Credit           = Cr\\u00E9dito\nAccountType.Equity           = Patrim\\u00F4nio l\\u00EDquido\nAccountType.Expense          = Despesa\nAccountType.Income           = Receita\nAccountType.Investment       = Investimento\nAccountType.Liability        = Passivo\nAccountType.MoneyMarket      = Mercado monet\\u00E1rio\nAccountType.Mutual           = Fundo m\\u00FAtuo\nAccountType.Root             = Raiz\nAccountType.SimpleInvestment = Investimento simples\n\nButton.AccTerms                = Usar termos cont\\u00E1beis\nButton.Accounts                = Contas\nButton.AckSel                  = Reconhecer selecionado\nButton.Add                     = Adicionar\nButton.AllDates                = Todas as Datas\nButton.Amortize                = Amortizar\nButton.AnimationsEnabled       = Ativar efeitos de anima\\u00E7\\u00E3o\nButton.AnyStatus               = Qualquer Status\nButton.Apply                   = Aplicar\nButton.AssetAccounts           = Contas de ativos\nButton.AutoReconcile           = Reconciliar automaticamente as transa\\u00E7\\u00F5es divididas\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = T\\u00EDtulos dispon\\u00EDveis\nButton.Back                    = Voltar\nButton.BankAccounts            = Contas Banc\\u00E1rias\nButton.BudgetMgr               = Gerente de Or\\u00E7amento\nButton.Budgeting               = Or\\u00E7amenta\\u00E7\\u00E3o\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = Cancelar\nButton.CheckForUpdates         = Verificar novas atualiza\\u00E7\\u00F5es\nButton.CheckReminders          = Verificar lembretes\nButton.Clear                   = Limpar\nButton.ClearAll                = Limpar tudo\nButton.Cleared                 = Confirmado\nButton.Close                   = Fechar\nButton.Compare                 = Comparar\nButton.ConcatenateMemos        = Combinar Memos\nButton.ConfirmReminderDelete   = Confirmar remo\\u00E7\\u00E3o de lembretes\nButton.ConfirmTransDelete      = Confirmar remo\\u00E7\\u00E3o de transa\\u00E7\\u00F5es\nButton.CopyToClip              = Copiar para a \\u00E1rea de transfer\\u00EAncia\nButton.CreateTimeFile          = Criar c\\u00F3pias de seguran\\u00E7a ao sair\nButton.CreditAccounts          = Contas de Cr\\u00E9dito\nButton.Delete                  = Apagar\nButton.DeleteAll               = Apagar todos\nButton.DeleteWeekends          = Excluir fins de semana\nButton.DetailSplits            = Exbir detalhes das divis\\u00F5es\nButton.Duplicate               = Duplicar\nButton.Edit                    = Editar\nButton.EnableAutoComplete      = Habilitar autocompletar\nButton.Enabled                 = Habilitado\nButton.EndingBalance           = Balan\\u00E7o final\nButton.Enter                   = Entrar\nButton.EnterDaysBefore         = Registrar dias de vencimento automaticamente\nButton.ExcludeFromBudget       = Excluir dos or\\u00E7amentos\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = Contas de Despesas\nButton.Export                  = Exportar\nButton.ExportSpreadsheet       = Exportar planilha\nButton.Filter                  = Filtrar\nButton.Finish                  = Finalizar\nButton.FinishLater             = Finalizar depois\nButton.ForceDefaultCurrency    = For\\u00E7ar uso da Moeda Padr\\u00E3o\nButton.ForceGC                 = Fo\\u00E7ar a coleta de lixo\nButton.HTTPAuth                = Requer autentica\\u00E7\\u00E3o\nButton.Hidden                  = Oculto\nButton.HideAccount             = Oculta\nButton.HideLockedAccount       = Ocultar contas bloqueadas\nButton.HidePlaceholderAccount  = Ocultar contas de categoria\nButton.HideZeroBalance         = Ocultar contas com balan\\u00E7o zero\nButton.HistoricalFill          = Preenchimento hist\\u00F3rico\nButton.Horizontal              = Horizontal\nButton.IncludeSubAccounts      = Incluir subcontas\nButton.IncomeAccounts          = Contas de Receitas\nButton.IncomeAndExpense        = Rendimento e Despesas\nButton.Insert                  = Inserir\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = Inverter sele\\u00E7\\u00E3o\nButton.Jump                    = Ir para\nButton.KeepFridays             = Mantenha sextas-feiras\nButton.KeepMondays             = Mantenha segundas-feiras\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u00DAltimos 120 dias\nButton.Last12Months            = \\u00DAltimos 12 meses\nButton.Last30Days              = \\u00DAltimos 30 dias\nButton.Last60Days              = \\u00DAltimos 60 dias\nButton.Last90Days              = \\u00DAltimos 90 dias\nButton.LiabilityAccounts       = Contas de Responsabilidade Civil\nButton.Locked                  = Bloqueada\nButton.MatchAccountOnly        = Corresponder usando somente transa\\u00E7\\u00F5es espec\\u00EDficas de conta\nButton.MatchAllTrans           = Corresponder usando todas as transa\\u00E7\\u00F5es\nButton.MatchCaseSensitive      = Correspond\\u00EAncia diferencia mai\\u00FAsculas\nButton.Modify                  = Modificar\nButton.MonthlyBalance          = Balan\\u00E7o Mensal\nButton.New                     = Novo\nButton.NewEmpty                = Novo vazio\nButton.NewHist                 = New Historical\nButton.NewPayment              = Novo pagamento\nButton.Next                    = Pr\\u00F3ximo\nButton.No                      = N\\u00E3o\nButton.NoEndDate               = Sem data final\nButton.None                    = Nenhum\nButton.NotReconciled           = N\\u00E3o Reconciliado\nButton.Ok                      = Aceitar\nButton.Open                    = Abrir\nButton.OpenLastOnStartup       = Abrir o \\u00FAltimo arquivo na inicializa\\u00E7\\u00E3o\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = Configurar p\\u00E1gina\nButton.PlaceHolder             = Categoria\nButton.Portrait                = Portrait\nButton.Print                   = Imprimir\nButton.PrintSample             = Imprimir exemplo\nButton.Properties              = Propriedades\nButton.Reconcile               = Reconciliar\nButton.ReconcileBoth           = Todas as contas de uma transa\\u00E7\\u00E3o possuem o mesmo estado de reconcilia\\u00E7\\u00E3o\nButton.ReconcileDisable        = Desabilitar reconcilia\\u00E7\\u00E3o autom\\u00E1tica\nButton.ReconcileIncomeExpense  = Reconciliar automaticamente as contas de Receitas e Despesas\nButton.Reconciled              = Reconciliado\nButton.Refresh                 = Atualizar\nButton.RegDate                 = Lembrar a data da \\u00FAltima transa\\u00E7\\u00E3o\nButton.Register                = Registro\nButton.RegisterFollowsList     = O registro segue a lista de contas\nButton.RememberPassword        = Lembrar senha\nButton.RemindLater             = Lembrar-me depois\nButton.Reminders               = Lembretes\nButton.RemoteServer            = Servidor remoto\nButton.Remove                  = Remover\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = Redimensionar\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = Correndo Balance\nButton.Save                    = Salvar\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = Selecionar\nButton.SelectAll               = Selecionar todos\nButton.SelectText              = Selecionar texto em foco\nButton.ShowCommodities         = Exibir todas as commodities\nButton.ShowEmptyAccounts       = Exibir contas com balan\\u00E7o zero\nButton.ShowPercentValues       = Exibir valores em porcentagem\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = Divis\\u00F5es\nButton.Start                   = Start\nButton.Stop                    = Parar\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = Temas habilitado\nButton.Timestamp               = Data/Hora\nButton.Today                   = Hoje\nButton.UpdateCurrenciesStartup = Atualizar cota\\u00E7\\u00F5es ao iniciar\nButton.UpdateOnline            = Atualizar Online\nButton.UpdateSecuritiesStartup = Atualizar t\\u00EDtulos ao iniciar\nButton.UseDailyRate            = Usar \\u00EDndice peri\\u00F3dico di\\u00E1rio\nButton.UseEncryption           = Usar encripta\\u00E7\\u00E3o\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = Usar nomes longos\nButton.UseProxy                = Usar proxy\nButton.UseRegexForFilter       = Use express\\u00F5es regulares para os filtros\nButton.Vertical                = Vertical\nButton.Yes                     = Sim\nButton.Zoom                    = Zoom\n\nColumn.Account                        = Conta\nColumn.AccountName                    = Nome da conta\nColumn.Action                         = A\\u00E7\\u00E3o\nColumn.Actual                         = Actual\nColumn.Amount                         = Valor\nColumn.Approve                        = Aprovar\nColumn.Balance                        = Balan\\u00E7o\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = Despesa\nColumn.Close                          = Fechar\nColumn.Clr                            = Rec\nColumn.Code                           = Code\nColumn.Commodity                      = Commodity\nColumn.CostBasis                      = Cost Basis\nColumn.Credit                         = Cr\\u00E9dito\nColumn.Currency                       = Moeda\nColumn.Date                           = Data\nColumn.Day                            = Dia\nColumn.Debit                          = D\\u00E9bito\nColumn.Decrease                       = Diminui\\u00E7\\u00E3o\nColumn.Deposit                        = Dep\\u00F3sito\nColumn.Description                    = Descri\\u00E7\\u00E3o\nColumn.Due                            = Due\nColumn.Enabled                        = Habilitado\nColumn.Entries                        = Entradas\nColumn.Event                          = Event\nColumn.ExchangeRate                   = Cota\\u00E7\\u00E3o\nColumn.Expense                        = Despesa\nColumn.Freq                           = Frequ\\u00EAncia\nColumn.Gain                           = Ganho\nColumn.High                           = Alto\nColumn.Income                         = Receita\nColumn.Increase                       = Aumento\nColumn.Investment                     = Investimento\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = Perda\nColumn.Low                            = Baixo\nColumn.Memo                           = Nota\nColumn.MktValue                       = Valor de mercado\nColumn.Month                          = M\\u00EAs\nColumn.Num                            = N\\u00FAm\nColumn.Payee                          = Benefici\\u00E1rio\nColumn.Payment                        = Pagamento\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = Pre\\u00E7o\nColumn.Print                          = Imprimir\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = Quantidade\nColumn.Rebate                         = Desconto\nColumn.Receive                        = Recebimento\nColumn.ReconciledBalance              = Balan\\u00E7o reconciliado\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qtd\nColumn.Short.RealizedGain             = Ganho R\nColumn.Short.RealizedGainPercentage   = Ganho R%\nColumn.Short.TotalGain                = Ganho T\nColumn.Short.TotalGainPercentage      = Ganho T%\nColumn.Short.UnrealizedGain           = Ganho NR\nColumn.Short.UnrealizedGainPercentage = Ganho NR%\nColumn.Spend                          = Gasto\nColumn.Timestamp                      = Carimbo de data / hora\nColumn.Total                          = Total\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = Tipo\nColumn.Value                          = Value\nColumn.Volume                         = Volume\nColumn.Withdrawal                     = Saque\n\nDataStoreType.Bxds = Arquivo bin\\u00E1rio\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = Arquivo XML\n\nItem.Amount         = Valor\nItem.CashDeposit    = Dep\\u00F3sito em dinheiro\nItem.CashWithdrawal = Saque em dinheiro\nItem.Date           = Data\nItem.EFT            = EFT\nItem.Memo           = Nota\nItem.NextNum        = Pr\\u00F3ximo #\nItem.Payee          = Benefici\\u00E1rio\nItem.Trans          = Trans\n\nLabel.AccentColor         = Cor do acento:\nLabel.Account             = Conta:\nLabel.AccountCode         = C\\u00F3digo da conta:\nLabel.AccountNumber       = N\\u00FAmero de conta:\nLabel.AccountOptions      = Op\\u00E7\\u00F5es da conta:\nLabel.AccountSeparator    = Separador de contas:\nLabel.AccountStatus       = Estado da conta:\nLabel.AccountType         = Tipo de conta:\nLabel.Action              = A\\u00E7\\u00E3o:\nLabel.Amount              = Valor:\nLabel.AnIntRate           = \\u00CDndice de interesse anual (APR):\nLabel.Available           = Dispon\\u00EDvel:\nLabel.Balance             = Balan\\u00E7o:\nLabel.BankAccount         = Conta Banc\\u00E1ria:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = Conta Pai:\nLabel.BaseColor           = Cor Base:\nLabel.Bottom              = Bottom:\nLabel.By                  = Por:\nLabel.CashBalance         = Balan\\u00E7o de caixa:\nLabel.Close               = Fechar:\nLabel.Color=Color:\nLabel.Commodity           = Commodity:\nLabel.CompDaysPerYear     = Dias compostos por ano:\nLabel.CompPerTerm         = Per\\u00EDodos compostos por ano:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = N\\u00FAmero:\nLabel.CreateCurr          = Criar moeda personalizada:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = Moedas / Commodity:\nLabel.Currencies          = Moedas:\nLabel.Currency            = Moeda:\nLabel.Current             = Corrente:\nLabel.DatabaseBackend     = Backend do banco de dados:\nLabel.DatabaseName        = Localiza\\u00E7\\u00E3o do banco de dados:\nLabel.DatabaseServer      = Servidor do banco de dados:\nLabel.Date                = Data:\nLabel.DateFormat          = Formato da data:\nLabel.DaysPastDue         = Dias em atraso:\nLabel.DefaultCurrency     = Moeda padr\\u00E3o:\nLabel.DelaySec            = Atraso (segundos)\nLabel.Description         = Descri\\u00E7\\u00E3o:\nLabel.DestAccount         = Conta destino:\nLabel.Difference          = Diferen\\u00E7a:\nLabel.Dividend            = Dividendo:\nLabel.EndDate             = Data final:\nLabel.EndOn               = Termina em:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = Balan\\u00E7o final:\nLabel.EquityAccount       = Conta de Patrim\\u00F4nio L\\u00EDquido:\nLabel.EscrowPmi           = Escrow, PMI, etc:\nLabel.Event               = Event:\nLabel.Every               = Cada:\nLabel.ExchangeAmount      = Montante da troca:\nLabel.ExchangeRate        = Taxa de c\\u00E2mbio:\nLabel.ExchangedAmount     = Montante trocado:\nLabel.Fees                = Taxas:\nLabel.FeesAccount         = Conta de Taxas:\nLabel.FileName            = Arquivo:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = Dia do primeiro pagamento:\nLabel.FocusColor          = Concentre Cor:\nLabel.Format              = Format:\nLabel.Frequency           = Frequ\\u00EAncia:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = Ganhos:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = Altura:\nLabel.High                = Alto:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = Conta de Receitas:\nLabel.InterestAccount     = Conta de Interesses:\nLabel.LastOccurrence      = \\u00DAltima ocorr\\u00EAncia:\nLabel.Layout              = Disposi\\u00E7\\u00E3o:\nLabel.Left                = Left:\nLabel.LoanTerm            = Prazo do empr\\u00E9stimo (meses):\nLabel.Low                 = Baixo:\nLabel.MarketValue         = Valor de mercado:\nLabel.MaxBackupCount      = N\\u00FAmero m\\u00E1ximo para manter de c\\u00F3pias de seguran\\u00E7a:\nLabel.Memo                = Nota:\nLabel.Monospace           = Mono-espa\\u00E7ada:\nLabel.Name                = Nome:\nLabel.NetIncome           = Receita l\\u00EDquida:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = Pr\\u00F3xima data de pagamento:\nLabel.Notes               = Notas:\nLabel.NumTrans            = N\\u00FAmero de transa\\u00E7\\u00F5es:\nLabel.Number              = N\\u00FAmero:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = Data da declara\\u00E7\\u00E3o de abertura:\nLabel.OpeningBalance      = Balan\\u00E7o inicial:\nLabel.OrigLoanAmt         = Valor original do empr\\u00E9stimo:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = Senha:\nLabel.Path                = Caminho\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = Pagamentos por ano:\nLabel.Payee               = Benefici\\u00E1rio:\nLabel.Period              = Period\nLabel.Port                = Porta:\nLabel.Prefix              = Prefixo:\nLabel.Price               = Pre\\u00E7o:\nLabel.Proportional        = Proporcional:\nLabel.Quantity            = Quantidade:\nLabel.QuoteSource         = Fonte de cita\\u00E7\\u00E3o:\nLabel.ReceivingAccount    = Conta de Recebimento:\nLabel.ReconciledBalance   = Balan\\u00E7o reconciliado:\nLabel.RemindLater         = Lembra-me novamente depois de:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = Repetir em:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Moeda reportada:\nLabel.Resolution          = Resolu\\u00E7\\u00E3o:\nLabel.ReturnOfCapital     = Returno de capital:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = Escala:\nLabel.Securities          = T\\u00EDtulos:\nLabel.Security            = T\\u00EDtulo:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = Exibir contas vazias\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = Data inicial:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = Posi\\u00E7\\u00E3o inicial:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = Data do Documento:\nLabel.StorageType         = Tipo de armazenamento:\nLabel.Suffix              = Sufixo:\nLabel.Symbol              = S\\u00EDmbolo:\nLabel.TargetBalance       = Balan\\u00E7o alvo:\nLabel.Top                 = Top:\nLabel.Total               = Total:\nLabel.Transaction         = Transa\\u00E7\\u00E3o:\nLabel.TransferFrom        = Transferir de:\nLabel.TransferTo          = Transferir para:\nLabel.Type                = Tipo:\nLabel.Units               = Units:\nLabel.UserName            = Nome do usu\\u00E1rio:\nLabel.Value               = Value:\nLabel.Verify              = Verificar:\nLabel.VerifyPassword      = Verificar senha:\nLabel.Visible             = Vis\\u00EDvel:\nLabel.Volume              = Volume:\nLabel.Width               = Width:\nLabel.XMLFiles            = Arquivos XML\nLabel.Year                = Year:\nLabel.jGnashFiles         = Arquivos jGnash\n\nMenu.About.Name                       = _Sobre\\u2026\nMenu.About.Tooltip                    = Informa\\u00E7\\u00F5es sobre o jGnash\nMenu.Account.Name                     = Conta\nMenu.AccountRegister.Name             = Registro de contas...\nMenu.AddRemoveCurrency.Name=_Adicionar / Remover\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = Atualizar todas as moedas\nMenu.BackgroundCurrencyUpdate.Tooltip = Atualizar todas as cota\\u00E7\\u00F5es em background\nMenu.BackgroundSecurityUpdate.Name    = Atualizar todos os t\\u00EDtulos\nMenu.BackgroundSecurityUpdate.Tooltip = Atualizar todos os pre\\u00E7os dos t\\u00EDtulos em background\nMenu.BalanceSheet.Name                = Balan\\u00E7o geral...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = Confirmado\nMenu.Close.Name                       = _Fechar\nMenu.Close.Tooltip                    = Fechar o arquivo ativo\nMenu.CloseAllWindows.Name             = Fechar todas as janelas\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = Janela do console\\u2026\nMenu.Copy.Name                        = _Copiar\nMenu.Copy.Tooltip                     = Criar uma c\\u00F3pia do item selecionado\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _Moedas\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = Mudar o _padr\\u00E3o...\nMenu.DefaultCurrency.Tooltip          = Mudar a moeda padr\\u00E3o\nMenu.Delete.Name                      = Apagar\nMenu.Duplicate.Name                   = Duplicata\nMenu.Edit.Name                        = _Editar\nMenu.EditTranNumList.Name             = Editar lista de n\\u00FAmeros da transa\\u00E7\\u00E3o\nMenu.Exit.Name                        = Sai_r\nMenu.Exit.Tooltip                     = Sair do jGnash\nMenu.Export.Name                      = _Exportar\nMenu.ExportAccounts.Name              = Exportar contas...\nMenu.File.Archive                     = Arquivar...\nMenu.File.Name                        = Arquivo\nMenu.Filter.Name                      = _Filtros\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = Aj_uda\nMenu.Hide.Name                        = Ocultar\nMenu.HistoryChart.Name                = Gr\\u00E1fico hist\\u00F3rico...\nMenu.HistoryCommodity.Name            = _Hist\\u00F3rico...\nMenu.HistoryImport.Name               = Hist\\u00F3rico de importa\\u00E7\\u00E3o...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = Gr\\u00E1fico de pizza de Receitas / Despesas...\nMenu.Import.Name                      = _Importar\nMenu.ImportAccounts.Name              = Importar contas...\nMenu.ImportAccounts.Tooltip           = Importar um arquivo de contas jGnash\nMenu.ImportJgnash.Name                = Importar jGnash...\nMenu.ImportJgnash.Tooltip             = Importar arquivos do jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Importar arquivos SWIFT MT940\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Importar arquivos OFX e QFX\nMenu.ImportQif.Name                   = _QIF...\nMenu.Jump.Name                        = Ir para\nMenu.ListOfAccounts.Name              = _Lista de contas\\u2026\nMenu.Locale.Name                      = Alterar localiza\\u00E7\\u00E3o...\nMenu.LookAndFeel.Name                 = Apar\\u00EAncia\nMenu.MarkAs.Name                      = Marcar como\nMenu.Modify.Name                      = Modificar\nMenu.ModifyCommodity.Name             = _Criar / Modificar...\nMenu.ModifyCurrency.Name              = _Modificar...\nMenu.ModifyExchangeRates.Name         = Editar taxas de c\\u00E2mbio...\nMenu.MonthBalance.Name                = Balan\\u00E7o mensal...\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = Balan\\u00E7o de fim de m\\u00EAs...\nMenu.MonthEndBalanceCSV.Name          = Balan\\u00E7o de fim de m\\u00EAs (CSV)...\nMenu.NetWorth.Name                    = Patrim\\u00F4nio l\\u00EDquido...\nMenu.New.Name                         = _Novo\\u2026\nMenu.New.Tooltip                      = Criar um novo arquivo\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _Abrir\\u2026\nMenu.Open.Tooltip                     = Abrir o arquivo especificado\nMenu.Option.Name                      = _Op\\u00E7\\u00F5es...\nMenu.Option.Tooltip                   = Alterar op\\u00E7\\u00F5es do programa\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = Gr\\u00E1fico de piza de Receitas / Depesas por benefici\\u00E1rio\\u2026\nMenu.PeriodicAccountBalance.Name      = Saldo em Conta peri\\u00F3dica\\u2026\nMenu.Portfolio.Name                   = Portf\\u00F3lio...\nMenu.ProfitLoss.Name                  = Lucros e preju\\u00EDzos...\nMenu.ProfitLossTXT.Name               = Lucros e preju\\u00EDzos (texto)...\nMenu.Reconcile.Name                   = Reconciliar\nMenu.Reconciled.Name                  = Reconciliado\nMenu.RecurringList.Name               = Transa\\u00E7\\u00F5es recorrentes...\nMenu.Register.Name                    = _Registrar...\nMenu.Reports.Name                     = _Relat\\u00F3rios\nMenu.RunJavaScript.Name               = Executar JavaScript...\nMenu.RunJavaScript.Tooltip            = Executar JavaScript\nMenu.Save.Name                        = _Salvar\nMenu.Save.Tooltip                     = Salvar o arquivo atual\nMenu.SaveAs.Name                      = Salvar _como...\nMenu.SaveAs.Tooltip                   = Salvar o arquivo atual com um novo nome\nMenu.Securities.Name                  = T\\u00EDtul_os\nMenu.SetPassword.Name                 = Definir senha...\nMenu.Show.Name                        = Exibir\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = Temas\nMenu.Tools.Name                       = _Ferramentas\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = N\\u00E3o reconciliado\nMenu.View.Name                        = E_xibir\nMenu.Window.Name                      = Janelas\n\nMessage.AcceptLicense                = Eu li, entendi e aceitei os termos das licen\\u00E7as.\nMessage.AccountAdd                   = Conta adicionada\nMessage.AccountCode                  = C\\u00F3digo da conta n\\u00E3o \\u00E9 \\u00FAnico: O c\\u00F3digo n\\u00E3o foi alterado\nMessage.AccountLocked                = A conta est\\u00E1 bloqueada\nMessage.AccountModify                = Conta modificada\nMessage.AccountMoveFailed            = N\\u00E3o foi poss\\u00EDvel mover a conta\nMessage.AccountRemove                = Conta removida\nMessage.AntiAlias                    = Suaviza\\u00E7\\u00E3o de texto habilidada!\nMessage.AutoSaveOff                  = Auto-grava\\u00E7\\u00E3o desativada\nMessage.AutoSaveOn                   = Auto-grava\\u00E7\\u00E3o ativada\nMessage.CSVFile                      = Arquivo separado por v\\u00EDrgulas (*.csv)\nMessage.CheckRecurring               = Verificando novos eventos recorrentes\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = Coletanto dados do relat\\u00F3rio\nMessage.CompilingReport              = Compilando relat\\u00F3rio\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = Apagar as transa\\u00E7\\u00F5es selecionadas?\nMessage.ConfirmReminderDelete        = Apagar os lembretes selecionados?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = Apagar a transa\\u00E7\\u00E3o selecionada?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = Moeda padr\\u00E3o alterada para:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = Motor iniciado\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = N\\u00E3o foi poss\\u00EDvel criar a conta\nMessage.Error.AccountRemove          = N\\u00E3o foi poss\\u00EDvel remover a conta\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = N\\u00E3o foi poss\\u00EDvel adicionar a commodity\nMessage.Error.AddCurrency            = N\\u00E3o foi poss\\u00EDvel adicionar a moeda\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = Crie primeiramente um conjunto b\\u00E1sico de contas\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicatas n\\u00E3o s\\u00E3o permitidas\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = O arquivo n\\u00E3o foi encontrado\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = A licen\\u00E7a n\\u00E3o foi aceita\nMessage.Error.LoadingFile            = Erro ao carregar o arquivo\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = N\\u00E3o foi poss\\u00EDvel modificar a commodity\nMessage.Error.ModifyCurrency         = N\\u00E3o foi poss\\u00EDvel modificar a moeda\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Nenhum transa\\u00E7\\u00E3o foi analisada\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = A conex\\u00E3o ao servidor falhou\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = Arquivo fechado\nMessage.FileIsLocked                 = O arquivo est\\u00E1 bloqueado por outro programa\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = O arquivo selecionado n\\u00E3o \\u00E9 v\\u00E1lido\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Por favor aguarde, a importa\\u00E7\\u00E3o pode demorar um pouco\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requer a JVM 11 ou superior\nMessage.LoadReportFail               = N\\u00E3o foi poss\\u00EDvel ler a defini\\u00E7\\u00E3o do relat\\u00F3rio\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = Localiza\\u00E7\\u00E3o padr\\u00E3o alterada para:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = N\\u00E3o repetir\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = O banco de dados existente ser\\u00E1 substitu\\u00EDdo\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = A an\\u00E1lise da defini\\u00E7\\u00E3o do relat\\u00F3rio falhou\nMessage.PleaseWait                   = Por favor, aguarde\nMessage.PrefFail                     = As prefer\\u00EAncias anteriores n\\u00E3o foram encontradas\nMessage.PrepShutdown                 = Preparando-se para desligar\nMessage.ProcessingReportData         = Processando os dados do relat\\u00F3rio\nMessage.Proxy                        = Configurando o proxy http:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = Os modulos de relat\\u00F3rios foram lidos com sucesso\nMessage.ReportWait                   = Por favor, aguarde. Carregando os m\\u00F3dulos de relat\\u00F3rios\nMessage.RestartLocale                = Reinicie o programa para a mudan\\u00E7a de localiza\\u00E7\\u00E3o ter efeito\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Buscando, por favor aguarde\nMessage.Shutdown                     = T\\u00E9rmino\nMessage.StartEndDate                 = Informe a data inicial e a final\nMessage.StoreBackup                  = Salvando c\\u00F3pia de seguran\\u00E7a em:\nMessage.StoreComplete                = O armazenamento do arquivo est\\u00E1 completo\nMessage.StoreWait                    = Esperando o completo armazenamento do arquivo\nMessage.TXTFile                      = Arquivos de texto (*.txt)\nMessage.TransactionAccountLocked     = Falha ao adicionar transa\\u00E7\\u00E3o, a conta de destino est\\u00E1 bloqueada\nMessage.TransactionAdd               = Transa\\u00E7\\u00E3o adicionada\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = Transa\\u00E7\\u00E3o removida\nMessage.TransactionRemoveLocked      = Falha ao remover transa\\u00E7\\u00E3o, a(s) conta(s) de destino est\\u00E1(\\u00E3o) bloqueada(s)\nMessage.UninstallBad                 = N\\u00E3o foi poss\\u00EDvel desinstalar com sucesso\nMessage.UninstallGood                = Desinstalado com sucesso!\nMessage.UpdatedPrice                 = O pre\\u00E7o para {0} foi atualizado\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = Voc\\u00EA est\\u00E1 usando a vers\\u00E3o\nMessage.Warn.CommodityInUse          = A commodity est\\u00E1 em uso\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = A moeda est\\u00E1 em uso\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = Contas Banc\\u00E1rias\nName.ExpenseAccounts = Contas de Despesas\nName.IncomeAccounts  = Contas de Receitas\nName.Root            = Raiz\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = De {0,date,long} at\\u00E9 {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = P\\u00E1gina {0} de {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 minutos\nPeriod.15Min     = 15 minutos\nPeriod.1Day      = 1 dia\nPeriod.1Hr       = 1 hora\nPeriod.2Hr       = 2 horas\nPeriod.30Min     = 30 minutos\nPeriod.5Min      = 5 minutos\nPeriod.8Hr       = 8 horas\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = Diariamente\nPeriod.Monthly   = Mensalmente\nPeriod.NextStart = Na pr\\u00F3xima vez que o jGnash iniciar\nPeriod.None      = Nenhum\nPeriod.OnlyOnce  = Uma vez\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = Semanalmente\nPeriod.Yearly    = Anualmente\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = Nenhum\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Austr\\u00E1lia\nQuoteSource.YahooUK  = Yahoo! Reino Unido e Irlanda\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = Sobre\nTab.Accounts        = Accounts\nTab.Adjust          = Ajuste\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = Despesa\nTab.Credit          = Cr\\u00E9dito\nTab.Credits         = Cr\\u00E9ditos\nTab.DataEngine      = Motor de dados\nTab.DataProviders   = Data Providers\nTab.Day             = Dia\nTab.Debit           = D\\u00E9bito\nTab.Formats         = Formats\nTab.GPLLicense      = Licen\\u00E7a GPL\nTab.General         = Geral\nTab.LGPLLicense     = Licen\\u00E7a LGPL\nTab.License         = Licen\\u00E7a\nTab.Month           = M\\u00EAs\nTab.Network         = Network\nTab.None            = Nenhum\nTab.Payment         = Pagamento\nTab.Register        = Registrar\nTab.Reminders       = Lembretes\nTab.Report          = Relat\\u00F3rio\nTab.StartupShutdown = In\\u00EDcio / T\\u00E9rmino\nTab.SysInfo         = Informa\\u00E7\\u00F5es do sistema\nTab.Transfer        = Transfer\\u00EAncia\nTab.Week            = Semana\nTab.Year            = Ano\n\nTag.Bank                   = Banco\nTag.Dividend               = Dividendo\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = Ganhos / (Perdas)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = Taxa de investimento\nTag.InvestmentCashTransfer = Investimento de transfer\\u00EAncia de renda\nTag.InvestmentFee          = Taxa de investimento\nTag.LTCG                   = Distribui\\u00E7\\u00E3o de longo prazo de ganho de capital\nTag.MTCG                   = Distribui\\u00E7\\u00E3o de m\\u00E9dio prazo de ganho de capital\nTag.Misc                   = Misc\nTag.NonTaxableInterest     = Passivos n\\u00E3o tax\\u00E1veis\nTag.STCG                   = Distribui\\u00E7\\u00E3o de curto prazo de ganho de capital\nTag.TaxableInterest        = Passivos tax\\u00E1veis\nTag.Vat                    = Vat\n\nTitle.About                      = Sobre\nTitle.AccountBalance             = Balan\\u00E7o mensal\nTitle.AccountFilter              = Filtros de conta\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = Informa\\u00E7\\u00E3o da conta\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = Conta de T\\u00EDtulos\nTitle.AddRemCurr                 = Adicionar / Remover moedas\nTitle.AmortizationSetup          = Configurar amortiza\\u00E7\\u00E3o\nTitle.Archive                    = Arquivar\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = Salvar automaticamente\nTitle.Available                  = Dispon\\u00EDvel\nTitle.BackgroundUpdate           = Atualiza\\u00E7\\u00F5es em background\nTitle.BackingStore               = Armazenamento\nTitle.BalanceSheet               = Balan\\u00E7o geral\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = Desenhador de cheques\nTitle.ChooseAccounts             = Escolha as contas para criar\nTitle.ColVis                     = Visibilidade das colunas\nTitle.Colors                     = Cores\nTitle.CommoditiesSecurities      = T\\u00EDtulos\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = Confirmar\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = Janela do Console\nTitle.CreateModifyCommodities    = Criar / Modificar t\\u00EDtulos\nTitle.Credits                    = Cr\\u00E9ditos\nTitle.Currencies                 = Moedas\nTitle.Current                    = Corrente\nTitle.CutOffDate                 = Selecionar data de corte\nTitle.DatabaseCfg                = Configura\\u00E7\\u00E3o do banco de dados\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = D\\u00E9bitos\nTitle.DefDefCurr                 = Definir moeda padr\\u00E3o\nTitle.DefTranNum                 = N\\u00FAmero de transa\\u00E7\\u00E3o padr\\u00E3o\nTitle.DefaultBehavior            = Comportamento padr\\u00E3o\nTitle.Defaults                   = Padr\\u00F5es\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = Exibi\\u00E7\\u00E3o\nTitle.DuplicateTransaction       = Transa\\u00E7\\u00E3o duplicada\nTitle.DuplicateTransactionsFound = Transa\\u00E7\\u00E3o duplicada encontrada\nTitle.EditExchangeRates          = Editar taxas de c\\u00E2mbio\nTitle.EndMonthBalance            = Balan\\u00E7o de fim de m\\u00EAs\nTitle.EnterPassword              = Digite a senha\nTitle.Entry                      = Entrada\nTitle.Error                      = Erro\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = Taxa de c\\u00E2mbio\nTitle.FileImport                 = Selecione o arquivo para importar\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = Filtros\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = Fontes padr\\u00E3o\nTitle.Frequency                  = Frequ\\u00EAncia\nTitle.HTTPProxy                  = Proxy HTTP\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = Importa\\u00E7\\u00E3o hist\\u00F3rica\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = Importar um arquivo QIF parcial\nTitle.ImpSum                     = Resumo da importa\\u00E7\\u00E3o\nTitle.ImportOFX                  = Importar um arquivo OFX\nTitle.ImportTransactions         = Importar um transa\\u00E7\\u00E3o de um arquivo\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = informa\\u00E7\\u00F5es\nTitle.InvFees                    = Taxas de investimentos\nTitle.InvGainsLoss               = Ganhos / Perdas de investimentos\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modificar transa\\u00E7\\u00F5es\nTitle.ModOFXTrans                = Modificar transa\\u00E7\\u00F5es OFX\nTitle.ModQIFTrans                = Modificar transa\\u00E7\\u00F5es QIF\nTitle.ModifyAccount              = Modificar conta\nTitle.ModifyCurrencies           = Modificar moedas\nTitle.ModifyReminder             = Modificar lembrete\nTitle.ModifySecHistory           = Modificar hist\\u00F3rico de t\\u00EDtulos\nTitle.ModifyTransaction          = Modificar transa\\u00E7\\u00E3o\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = Nova conta\nTitle.NewBudget                  = Novo Or\\u00E7amento\nTitle.NewFile                    = Criar novo arquivo\nTitle.NewPassword                = New Password\nTitle.NewReminder                = Nova lembrete\nTitle.NewTrans                   = Nova transa\\u00E7\\u00E3o\nTitle.Notes                      = Notas\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = Abrir\nTitle.Options                    = Op\\u00E7\\u00F5es\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = Configurar p\\u00E1gina\nTitle.ParentAccount              = Conta Pai\nTitle.PercentDist                = Distribui\\u00E7\\u00E3o percentual\nTitle.PercentExpense             = Porcentagem de despeva\nTitle.PercentIncome              = Porcentagem de receita\nTitle.PleaseWait                 = Por favor, aguarde\nTitle.PortfolioReport            = Relat\\u00F3rio de portf\\u00F3lio\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = Declara\\u00E7\\u00E3o de lucros e preju\\u00EDzos\nTitle.ReconcileSettings          = Configura\\u00E7\\u00E3o de reconciliamento\nTitle.Reminder                   = Lembrete\nTitle.Reminders                  = Lembretes\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = Salvar como\nTitle.SaveFile                   = Salvar arquivo\nTitle.SecurityHistory            = Hist\\u00F3rico de t\\u00EDtulo\nTitle.SelAccount                 = Selecionar conta\nTitle.SelAvailCurr               = Selecionar moedas dispon\\u00EDveis\nTitle.SelDate                    = Selecionar uma data\nTitle.SelDefCurr                 = Selecionar moeda padr\\u00E3o\nTitle.SelDefLocale               = Selecionar localiza\\u00E7\\u00E3o padr\\u00E3o\nTitle.SelDestAccount             = Selecionar conta de destino\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = Selecionar conta de patrim\\u00F4nio\nTitle.SelFile                    = Selecionar arquivo\nTitle.SelQifDateFormat           = Selecionar formato de data QIF\nTitle.SelectColor                = Selecionar cores\nTitle.Selected                   = Selecionado\nTitle.Shutdown                   = T\\u00E9rmino\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = Dividir transa\\u00E7\\u00E3o\nTitle.Startup                    = Startup\nTitle.Steps                      = Passos\nTitle.Success                    = Sucesso\nTitle.Summary                    = Resumo\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = Transa\\u00E7\\u00E3o\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = Lista de transa\\u00E7\\u00F5es\nTitle.TransactionSetup           = Configurar transa\\u00E7\\u00E3o\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Exce\\u00E7\\u00E3o n\\u00E3o capturada\nTitle.ViewImage                  = View Image\nTitle.Visible                    = Vis\\u00EDvel\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = Lista de contas\nToolTip.AccountRegister                      = Registro de contas\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = Alterar visibilidade da coluna\nToolTip.ConvertSEntry                        = Converter esta transa\\u00E7\\u00E3o para uma transa\\u00E7\\u00E3o de partida dobrada\nToolTip.DeleteAccount                        = Apagar conta\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = Filtrar contas\nToolTip.FilterAccounts                       = Filtrar contas pelo tipo\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = Alterar o tamanho da fonte\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = N\\u00FAmero Internacional de Identifica\\u00E7\\u00E3o de T\\u00EDtulos\nToolTip.IntegersOnly                         = apenas n\\u00FAmeros inteiros permitido\nToolTip.ModifyAccount                        = Alterar conta\nToolTip.NewAccount                           = Nova conta\nToolTip.PageSetup                            = Configurar p\\u00E1gina\nToolTip.PrintRegRep                          = Imprimir relat\\u00F3rio de registro\nToolTip.ReconcileAccount                     = Reconciliar conta\nToolTip.Reminders                            = Lembretes\nToolTip.ResizeColumns                        = Redimensionar colunas\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = N\\u00FAmero de digitos a direita do decimal\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = Criar uma c\\u00F3pia de seguran\\u00E7a com a data quando fechado\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = Abrir um novo registro de conta\n\nTransaction.AddShare        = Adicionar a\\u00E7\\u00F5es (ajuste)\nTransaction.BuyShare        = Comprar a\\u00E7\\u00F5es\nTransaction.Dividend        = Dividendo\nTransaction.DoubleEntry     = Partidas dobradas\nTransaction.MergeShare      = Combinar stock\nTransaction.ReinvestDiv     = Reinvestir dividendo\nTransaction.RemoveShare     = Remover a\\u00E7\\u00F5es (ajuste)\nTransaction.ReturnOfCapital = Retorno de capital\nTransaction.SellShare       = Vender a\\u00E7\\u00F5es\nTransaction.SingleEntry     = Entrada \\u00FAnica\nTransaction.Split           = Dividir transa\\u00E7\\u00E3o\nTransaction.SplitEntry      = Dividir entrada da transa\\u00E7\\u00E3o\nTransaction.SplitShare      = Dividir a\\u00E7\\u00F5es\nTransaction.TransferIn      = Transfer\\u00EAncia de dinheiro em\nTransaction.TransferOut     = Transfer\\u00EAncia de dinheiro para\n\nWord.Add             = Adicionar\nWord.All             = Todos\nWord.Balance         = Balan\\u00E7o\nWord.Buy             = Comprar\nWord.Commodity       = Commodity\nWord.Copy            = Copy\nWord.Description     = Descri\\u00E7\\u00E3o\nWord.Difference      = Diferen\\u00E7a\nWord.Dividend        = Dividendo\nWord.Exchange        = C\\u00E2mbio\nWord.Fees            = Taxas\nWord.GrossExpense    = Despesa bruta\nWord.GrossIncome     = Receita bruta\nWord.Interest        = Interesse\nWord.Into            = em\nWord.Invalid         = Inv\\u00E1lido\nWord.Merge           = ir\nWord.Monthly         = Por m\\u00EAs\nWord.Name            = Nome\nWord.NetIncome       = Receita l\\u00EDquida\nWord.NetWorth        = Patrim\\u00F4nio l\\u00EDquido\nWord.NewBudget       = New Budget\nWord.None            = Nenhum\nWord.Quarterly       = Trimestralmente\nWord.ReInvDiv        = Reinvestir dividendo\nWord.Remove          = Remover\nWord.ReturnOfCapital = Retorno de capital\nWord.Seconds         = seconds\nWord.Security        = T\\u00EDtulos\nWord.Sell            = Vender\nWord.Split           = Split\nWord.Subtotal        = Subtotal\nWord.Total           = Total\nWord.Totals          = Totais\nWord.Yearly          = Anualmente\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_ru.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = \\u0418\\u043C\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u043E\nAccountType.Bank             = \\u0411\\u0430\\u043D\\u043A\\u043E\\u0432\\u0441\\u043A\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442\nAccountType.Cash             = \\u041D\\u0430\\u043B\\u0438\\u0447\\u043D\\u044B\\u0435\nAccountType.Checking         = \\u0427\\u0435\\u043A\\u043E\\u0432\\u044B\\u0439 \\u0441\\u0447\\u0435\\u0442\nAccountType.Credit           = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\nAccountType.Equity           = \\u0421\\u043E\\u0431\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0440\\u0435\\u0434\\u0441\\u0442\\u0432\\u0430\nAccountType.Expense          = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\nAccountType.Income           = \\u0414\\u043E\\u0445\\u043E\\u0434\nAccountType.Investment       = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u044F\nAccountType.Liability        = \\u041E\\u0431\\u044F\\u0437\\u0430\\u0442\\u0435\\u043B\\u044C\\u0441\\u0442\\u0432\\u043E\nAccountType.MoneyMarket      = \\u0414\\u0435\\u043D\\u0435\\u0436\\u043D\\u044B\\u0439 \\u0440\\u044B\\u043D\\u043E\\u043A\nAccountType.Mutual           = \\u0412\\u0437\\u0430\\u0438\\u043C\\u043D\\u044B\\u0439 \\u0444\\u043E\\u043D\\u0434\nAccountType.Root             = \\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nAccountType.SimpleInvestment = \\u041F\\u0440\\u043E\\u0441\\u0442\\u043E\\u0439 \\u0438\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u043E\\u043D\\u043D\\u044B\\u0439\n\nButton.AccTerms                = \\u0418\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u0431\\u0443\\u0445\\u0433\\u0430\\u043B\\u0442\\u0435\\u0440\\u0441\\u043A\\u0438\\u0435 \\u0442\\u0435\\u0440\\u043C\\u0438\\u043D\\u044B\nButton.Accounts                = \\u0421\\u0447\\u0435\\u0442\\u0430\nButton.AckSel                  = \\u041F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0438\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u043E\\u0435\nButton.Add                     = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C\nButton.AllDates                = \\u0412\\u0441\\u0435 \\u0414\\u0430\\u0442\\u044B\nButton.Amortize                = \\u0410\\u043C\\u043E\\u0440\\u0442\\u0438\\u0437\\u0430\\u0446\\u0438\\u044F\nButton.AnimationsEnabled       = \\u0410\\u043D\\u0438\\u043C\\u0430\\u0446\\u0438\\u044F\nButton.AnyStatus               = \\u041B\\u044E\\u0431\\u043E\\u0439 \\u0441\\u0442\\u0430\\u0442\\u0443\\u0441\nButton.Apply                   = \\u041F\\u0440\\u0438\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C\nButton.AssetAccounts           = \\u0418\\u043C\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.AutoReconcile           = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438 \\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u044B\\u0432\\u0430\\u0442\\u044C \\u0441\\u043F\\u043B\\u0438\\u0442-\\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nButton.AutoResizeColumns       = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438 \\u0418\\u0437\\u043C\\u0435\\u043D\\u044F\\u0442\\u044C \\u0420\\u0430\\u0437\\u043C\\u0435\\u0440 \\u0421\\u0442\\u043E\\u043B\\u0431\\u0446\\u043E\\u0432\nButton.AvailSecurities         = \\u0414\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u044B\\u0435 \\u0446\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438\nButton.Back                    = \\u041D\\u0430\\u0437\\u0430\\u0434\nButton.BankAccounts            = \\u0411\\u0430\\u043D\\u043A\\u043E\\u0432\\u0441\\u043A\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.BudgetMgr               = \\u041C\\u0435\\u043D\\u0435\\u0434\\u0436\\u0435\\u0440 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0430\nButton.Budgeting               = \\u0411\\u044E\\u0434\\u0436\\u0435\\u0442\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = \\u041E\\u0442\\u043C\\u0435\\u043D\\u0430\nButton.CheckForUpdates         = \\u041F\\u0440\\u043E\\u0432\\u0435\\u0440\\u043A\\u0430 \\u043E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u0439\nButton.CheckReminders          = \\u041F\\u0440\\u043E\\u0432\\u0435\\u0440\\u0438\\u0442\\u044C \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F\nButton.Clear                   = \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u044C\nButton.ClearAll                = \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u044C \\u0432\\u0441\\u0435\nButton.Cleared                 = \\u0427\\u0438\\u0441\\u0442\\u043E\\u0435\nButton.Close                   = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C\nButton.Compare                 = \\u0421\\u0440\\u0430\\u0432\\u043D\\u0438\\u0442\\u044C\nButton.ConcatenateMemos        = \\u041E\\u0431\\u044A\\u0435\\u0434\\u0438\\u043D\\u0438\\u0442\\u044C \\u0437\\u0430\\u043C\\u0435\\u0442\\u043A\\u0438\nButton.ConfirmReminderDelete   = \\u0417\\u0430\\u043F\\u0440\\u043E\\u0441 \\u043F\\u0440\\u0438 \\u0443\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0438 \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0439\nButton.ConfirmTransDelete      = \\u0417\\u0430\\u043F\\u0440\\u043E\\u0441 \\u043F\\u0440\\u0438 \\u0443\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nButton.CopyToClip              = \\u0421\\u043A\\u043E\\u043F\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0432 \\u0431\\u0443\\u0444\\u0435\\u0440 \\u043E\\u0431\\u043C\\u0435\\u043D\\u0430\nButton.CreateTimeFile          = \\u041F\\u0440\\u0438 \\u0432\\u044B\\u0445\\u043E\\u0434\\u0435 \\u0441\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B \\u0441 \\u043C\\u0435\\u0442\\u043A\\u043E\\u0439 \\u0434\\u0430\\u0442\\u044B\nButton.CreditAccounts          = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.Delete                  = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\nButton.DeleteAll               = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u0441\\u0435\nButton.DeleteWeekends          = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u044B\\u0445\\u043E\\u0434\\u043D\\u044B\\u0435 \\u0434\\u043D\\u0438\nButton.DetailSplits            = \\u0421\\u043F\\u043B\\u0438\\u0442...\nButton.Duplicate               = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043A\\u043E\\u043F\\u0438\\u044E\nButton.Edit                    = \\u0420\\u0435\\u0434\\u0430\\u043A\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\nButton.EnableAutoComplete      = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0430\\u0432\\u0442\\u043E-\\u0434\\u043E\\u043F\\u043E\\u043B\\u043D\\u0435\\u043D\\u0438\\u0435 \\u043F\\u0440\\u0438 \\u0432\\u0432\\u043E\\u0434\\u0435 \\u0442\\u0435\\u043A\\u0441\\u0442\\u0430\nButton.Enabled                 = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u043E\nButton.EndingBalance           = \\u041E\\u0441\\u0442\\u0430\\u0442\\u043E\\u043A \\u043D\\u0430 \\u043A\\u043E\\u043D\\u0435\\u0446 \\u043F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0430\nButton.Enter                   = \\u0412\\u0432\\u0435\\u0441\\u0442\\u0438\nButton.EnterDaysBefore         = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438 \\u0432\\u043D\\u043E\\u0441\\u0438\\u0442\\u044C \\u0437\\u0430 .. \\u0434\\u043D\\u0435\\u0439\nButton.ExcludeFromBudget       = \\u0418\\u0441\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0438\\u0437 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u043E\\u0432\nButton.ExecuteNow              = \\u0412\\u044B\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C \\u0441\\u0435\\u0439\\u0447\\u0430\\u0441\nButton.ExpenseAccounts         = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.Export                  = \\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\nButton.ExportSpreadsheet       = \\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442 \\u0442\\u0430\\u0431\\u043B\\u0438\\u0446\\u044B\nButton.Filter                  = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440\nButton.Finish                  = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u044C\nButton.FinishLater             = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u044C \\u043F\\u043E\\u0437\\u0434\\u043D\\u0435\\u0435\nButton.ForceDefaultCurrency    = \\u041F\\u0440\\u0438\\u043D\\u0443\\u0434\\u0438\\u0442\\u0435\\u043B\\u044C\\u043D\\u043E \\u0438\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nButton.ForceGC                 = \\u0417\\u0430\\u043F\\u0443\\u0441\\u0442\\u0438\\u0442\\u044C \\u0441\\u0431\\u043E\\u0440\\u0449\\u0438\\u043A \\u043C\\u0443\\u0441\\u043E\\u0440\\u0430\nButton.HTTPAuth                = \\u0422\\u0440\\u0435\\u0431\\u0443\\u0435\\u0442\\u0441\\u044F HTTP \\u0430\\u0443\\u0442\\u0435\\u043D\\u0442\\u0438\\u0444\\u0438\\u043A\\u0430\\u0446\\u0438\\u044F\nButton.Hidden                  = \\u0421\\u043A\\u0440\\u044B\\u0442\\u044B\\u0435\nButton.HideAccount             = \\u0421\\u043A\\u0440\\u044B\\u0442\\u044B\\u0439\nButton.HideLockedAccount       = \\u0421\\u043A\\u0440\\u044B\\u0442\\u044C \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.HidePlaceholderAccount  = \\u0421\\u043A\\u0440\\u044B\\u0442\\u044C \\u0433\\u0440\\u0443\\u043F\\u043F\\u044B\nButton.HideZeroBalance         = \\u0421\\u043A\\u0440\\u044B\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430 \\u0441 \\u043D\\u0443\\u043B\\u0435\\u0432\\u044B\\u043C \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u043C\nButton.HistoricalFill          = \\u0417\\u0430\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C \\u0438\\u0437 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438\nButton.Horizontal              = \\u0413\\u043E\\u0440\\u0438\\u0437\\u043E\\u043D\\u0442\\u0430\\u043B\\u044C\\u043D\\u044B\\u0439\nButton.IncludeSubAccounts      = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.IncomeAccounts          = \\u041F\\u0440\\u0438\\u0445\\u043E\\u0434\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.IncomeAndExpense        = \\u0414\\u043E\\u0445\\u043E\\u0434\\u044B \\u0438 \\u0440\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B\nButton.Insert                  = \\u0412\\u0441\\u0442\\u0430\\u0432\\u0438\\u0442\\u044C\nButton.InvertBalances          = \\u0418\\u043D\\u0432\\u0435\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u044B\nButton.InvertSelection         = \\u0418\\u043D\\u0432\\u0435\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u043E\\u0435\nButton.Jump                    = \\u041F\\u0435\\u0440\\u0435\\u0439\\u0442\\u0438\nButton.KeepFridays             = \\u041E\\u0441\\u0442\\u0430\\u0432\\u0438\\u0442\\u044C \\u043F\\u044F\\u0442\\u043D\\u0438\\u0446\\u044B\nButton.KeepMondays             = \\u041E\\u0441\\u0442\\u0430\\u0432\\u0438\\u0442\\u044C \\u043F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u0438\\u043A\\u0438\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0435 120 \\u0434\\u043D\\u0435\\u0439\nButton.Last12Months            = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0435 12 \\u043C\\u0435\\u0441\\u044F\\u0446\\u0435\\u0432\nButton.Last30Days              = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0435 30 \\u0434\\u043D\\u0435\\u0439\nButton.Last60Days              = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0435 60 \\u0434\\u043D\\u0435\\u0439\nButton.Last90Days              = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0435 90 \\u0434\\u043D\\u0435\\u0439\nButton.LiabilityAccounts       = \\u041E\\u0431\\u044F\\u0437\\u0430\\u0442\\u0435\\u043B\\u044C\\u0441\\u0442\\u0432\\u0430\nButton.Locked                  = \\u0417\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\nButton.MatchAccountOnly        = \\u041F\\u043E\\u0438\\u0441\\u043A \\u0442\\u043E\\u043B\\u044C\\u043A\\u043E \\u043F\\u043E \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F\\u043C \\u0441\\u0447\\u0435\\u0442\\u0430\nButton.MatchAllTrans           = \\u041F\\u043E\\u0438\\u0441\\u043A \\u043F\\u043E \\u0432\\u0441\\u0435\\u043C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F\\u043C\nButton.MatchCaseSensitive      = \\u041F\\u043E\\u0438\\u0441\\u043A \\u0447\\u0443\\u0432\\u0441\\u0442\\u0432\\u0438\\u0442\\u0435\\u043B\\u0435\\u043D \\u043A \\u0440\\u0435\\u0433\\u0438\\u0441\\u0442\\u0440\\u0443\nButton.Modify                  = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C\nButton.MonthlyBalance          = \\u0415\\u0436\\u0435\\u043C\\u0435\\u0441\\u044F\\u0447\\u043D\\u044B\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\nButton.New                     = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C\nButton.NewEmpty                = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u043F\\u0443\\u0441\\u0442\\u043E\\u0439\nButton.NewHist                 = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438\\u0439\nButton.NewPayment              = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u043F\\u043B\\u0430\\u0442\\u0435\\u0436\nButton.Next                    = \\u0421\\u043B\\u0435\\u0434\\u0443\\u044E\\u0449\\u0438\\u0439\nButton.No                      = \\u041D\\u0435\\u0442\nButton.NoEndDate               = \\u041D\\u0435\\u0442 \\u043A\\u043E\\u043D\\u0435\\u0447\\u043D\\u043E\\u0439 \\u0434\\u0430\\u0442\\u044B\nButton.None                    = \\u041D\\u0438\\u043A\\u0430\\u043A\\u0438\\u0435\nButton.NotReconciled           = \\u041D\\u0435\\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u043E\nButton.Ok                      = OK\nButton.Open                    = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C\nButton.OpenLastOnStartup       = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C \\u043F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B \\u043F\\u0440\\u0438 \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A\\u0435\nButton.Options                 = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438\nButton.Order.LinuxOS           = \\u041D\\u0435\\u0442, \\u0414\\u0430, [\\u041E\\u0442\\u043C\\u0435\\u043D\\u0430 | \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C | \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u044C], [OK | \\u0412\\u0432\\u043E\\u0434] (Linux)\nButton.Order.MacOS             = \\u041D\\u0435\\u0442, \\u0414\\u0430, [\\u041E\\u0442\\u043C\\u0435\\u043D\\u0430 | \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C | \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u044C], [OK | \\u0412\\u0432\\u043E\\u0434] (Mac)\nButton.Order.WindowsOS         = \\u0414\\u0430, \\u041D\\u0435\\u0442, [OK | \\u0412\\u0432\\u043E\\u0434], [\\u041E\\u0442\\u043C\\u0435\\u043D\\u0430 | \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C | \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u044C] (Windows)\nButton.PageSetup               = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u044B \\u0441\\u0442\\u0440\\u0430\\u043D\\u0438\\u0446\\u044B\nButton.PlaceHolder             = \\u0413\\u0440\\u0443\\u043F\\u043F\\u0430\nButton.Portrait                = Portrait\nButton.Print                   = \\u041F\\u0435\\u0447\\u0430\\u0442\\u044C\nButton.PrintSample             = \\u041F\\u0440\\u043E\\u0431\\u043D\\u0430\\u044F \\u043F\\u0435\\u0447\\u0430\\u0442\\u044C\nButton.Properties              = \\u0421\\u0432\\u043E\\u0439\\u0441\\u0442\\u0432\\u0430\nButton.Reconcile               = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u0442\\u044C\nButton.ReconcileBoth           = \\u0412\\u0441\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438 \\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u044B\\u0432\\u0430\\u044E\\u0442\\u0441\\u044F \\u043E\\u0434\\u0438\\u043D\\u0430\\u043A\\u043E\\u0432\\u043E\nButton.ReconcileDisable        = \\u041E\\u0442\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0430\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u043E\\u0435 \\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u0438\\u0435\nButton.ReconcileIncomeExpense  = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438 \\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u044B\\u0432\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430 \\u0414\\u043E\\u0445\\u043E\\u0434\\u043E\\u0432 \\u0438 \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u043E\\u0432\nButton.Reconciled              = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u043E\nButton.Refresh                 = \\u041E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C\nButton.RegDate                 = \\u0417\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u0442\\u044C \\u0434\\u0430\\u0442\\u0443 \\u043F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0435\\u0439 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nButton.Register                = \\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nButton.RegisterFollowsList     = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438\\u0439 \\u0432\\u044B\\u0431\\u043E\\u0440 \\u0441\\u0447\\u0435\\u0442\\u0430 \\u0432 \\u0440\\u0435\\u0435\\u0441\\u0442\\u0440\\u0435 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nButton.RememberPassword        = \\u0417\\u0430\\u043F\\u043E\\u043C\\u043D\\u0438\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nButton.RemindLater             = \\u041D\\u0430\\u043F\\u043E\\u043C\\u043D\\u0438\\u0442\\u044C \\u043F\\u043E\\u0437\\u0436\\u0435\nButton.Reminders               = \\u041D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F\nButton.RemoteServer            = \\u0423\\u0434\\u0430\\u043B\\u0435\\u043D\\u043D\\u044B\\u0439 \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440\nButton.Remove                  = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\nButton.RemoveOldBackups        = \\u0423\\u0434\\u0430\\u043B\\u044F\\u0442\\u044C \\u0441\\u0442\\u0430\\u0440\\u044B\\u0435 \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u044B\\u0435 \\u043A\\u043E\\u043F\\u0438\\u0438\nButton.Rename                  = \\u041F\\u0435\\u0440\\u0435\\u0438\\u043C\\u0435\\u043D\\u043E\\u0432\\u0430\\u0442\\u044C\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = \\u0421\\u0431\\u0440\\u043E\\u0441\\u0442\\u044C \\u0432\\u0441\\u0435\nButton.Resize                  = \\u0410\\u0432\\u0442\\u043E-\\u0440\\u0430\\u0437\\u043C\\u0435\\u0440\nButton.RestoreDefault          = \\u0423\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044F\nButton.RestoreLastTranTab      = \\u0412\\u043E\\u0441\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u043F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u044E\\u044E \\u0438\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u0443\\u0435\\u043C\\u0443 \\u0432\\u043A\\u043B\\u0430\\u0434\\u043A\\u0443 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nButton.RoundToWhole            = \\u041E\\u043A\\u0440\\u0443\\u0433\\u043B\\u0438\\u0442\\u044C \\u0434\\u043E \\u0446\\u0435\\u043B\\u044B\\u0445\nButton.RunningBalance          = \\u041A\\u0440\\u0438\\u0432\\u0430\\u044F \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u0430\nButton.Save                    = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C\nButton.SaveFilters             = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0444\\u0438\\u043B\\u044C\\u0442\\u0440\\u044B\nButton.SaveImage               = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0438\\u0437\\u043E\\u0431\\u0440\\u0430\\u0436\\u0435\\u043D\\u0438\\u0435\nButton.Select                  = \\u0412\\u044B\\u0431\\u0440\\u0430\\u0442\\u044C\nButton.SelectAll               = \\u0412\\u044B\\u0431\\u0440\\u0430\\u0442\\u044C \\u0432\\u0441\\u0435\nButton.SelectText              = \\u0412\\u044B\\u0434\\u0435\\u043B\\u044F\\u0442\\u044C \\u0442\\u0435\\u043A\\u0441\\u0442 \\u043F\\u0440\\u0438 \\u0432\\u044B\\u0431\\u043E\\u0440\\u0435\nButton.ShowCommodities         = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0432\\u0441\\u0435 \\u0431\\u0438\\u0440\\u0436\\u0435\\u0432\\u044B\\u0435 \\u0442\\u043E\\u0432\\u0430\\u0440\\u044B\nButton.ShowEmptyAccounts       = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430 \\u0441 \\u043D\\u0443\\u043B\\u0435\\u0432\\u044B\\u043C \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u043C\nButton.ShowPercentValues       = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u043F\\u0440\\u043E\\u0446\\u0435\\u043D\\u0442\\u044B\nButton.ShowTimestamp           = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u043C\\u0435\\u0442\\u043A\\u0443 \\u0432\\u0440\\u0435\\u043C\\u0435\\u043D\\u0438\nButton.Splits                  = \\u0421\\u043F\\u043B\\u0438\\u0442\nButton.Start                   = \\u0421\\u0442\\u0430\\u0440\\u0442\nButton.Stop                    = \\u041E\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C\nButton.SubstanceAnimations     = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0430\\u043D\\u0438\\u043C\\u0430\\u0446\\u0438\\u044E \\u0442\\u0435\\u043C Substance\nButton.SumColVis               = \\u041F\\u043E\\u043A\\u0430\\u0437\\u044B\\u0432\\u0430\\u0442\\u044C \\u0438\\u0442\\u043E\\u0433\\u043E\\u0432\\u044B\\u0435 \\u043A\\u043E\\u043B\\u043E\\u043D\\u043A\\u0438\nButton.SumRowVis               = \\u041F\\u043E\\u043A\\u0430\\u0437\\u044B\\u0432\\u0430\\u0442\\u044C \\u0438\\u0442\\u043E\\u0433\\u043E\\u0432\\u044B\\u0435 \\u0441\\u0442\\u0440\\u043E\\u043A\\u0438\nButton.ThemesEnabled           = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0438\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u043D\\u044B\\u0435 \\u0442\\u0435\\u043C\\u044B\nButton.Timestamp               = \\u0412\\u0440\\u0435\\u043C\\u0435\\u043D\\u043D\\u0430\\u044F \\u043C\\u0435\\u0442\\u043A\\u0430\nButton.Today                   = \\u0421\\u0435\\u0433\\u043E\\u0434\\u043D\\u044F\nButton.UpdateCurrenciesStartup = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u044F\\u0442\\u044C \\u043A\\u0443\\u0440\\u0441\\u044B \\u0432\\u0430\\u043B\\u044E\\u0442 \\u043F\\u0440\\u0438 \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A\\u0435\nButton.UpdateOnline            = \\u041F\\u043E\\u043B\\u0443\\u0447\\u0438\\u0442\\u044C \\u0438\\u0437 \\u0438\\u043D\\u0442\\u0435\\u0440\\u043D\\u0435\\u0442\nButton.UpdateSecuritiesStartup = \\u041E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u0446\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438 \\u043F\\u0440\\u0438 \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A\\u0435\nButton.UseDailyRate            = \\u0418\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u043F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0438\\u0447\\u0435\\u0441\\u043A\\u0438\\u0439 \\u043A\\u0443\\u0440\\u0441 \\u0437\\u0430 \\u0434\\u0435\\u043D\\u044C\nButton.UseEncryption           = \\u0418\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u0448\\u0438\\u0444\\u0440\\u043E\\u0432\\u0430\\u043D\\u0438\\u0435\nButton.UseFuzzyMatch           = \\u0418\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u043D\\u0435\\u0447\\u0435\\u0442\\u043A\\u0438\\u0439 \\u043F\\u043E\\u0438\\u0441\\u043A\nButton.UseLongNames            = \\u041F\\u043E\\u043B\\u043D\\u044B\\u0435 \\u0438\\u043C\\u0435\\u043D\\u0430 \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nButton.UseProxy                = \\u0418\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u043F\\u0440\\u043E\\u043A\\u0441\\u0438\nButton.UseRegexForFilter       = \\u0418\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u044C \\u0440\\u0435\\u0433\\u0443\\u043B\\u044F\\u0440\\u043D\\u044B\\u0435 \\u0432\\u044B\\u0440\\u0430\\u0436\\u0435\\u043D\\u0438\\u044F \\u0434\\u043B\\u044F \\u0444\\u0438\\u043B\\u044C\\u0442\\u0440\\u043E\\u0432\nButton.Vertical                = \\u0412\\u0435\\u0440\\u0442\\u0438\\u043A\\u0430\\u043B\\u044C\\u043D\\u044B\\u0439\nButton.Yes                     = \\u0414\\u0430\nButton.Zoom                    = \\u0412 \\u043E\\u043A\\u043D\\u0435\n\nColumn.Account                        = \\u0421\\u0447\\u0435\\u0442\nColumn.AccountName                    = \\u041D\\u0430\\u0437\\u0432\\u0430\\u043D\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nColumn.Action                         = \\u0414\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u0435\nColumn.Actual                         = \\u0424\\u0430\\u043A\\u0442\nColumn.Amount                         = \\u0421\\u0443\\u043C\\u043C\\u0430\nColumn.Approve                        = \\u041F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0436\\u0434\\u0435\\u043D\\u043E\nColumn.Balance                        = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\nColumn.Budgeted                       = \\u0411\\u044E\\u0434\\u0436\\u0435\\u0442\nColumn.Charge                         = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\nColumn.Close                          = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C\nColumn.Clr                            = \\u0421\\u043E\\u0433\\u043B\nColumn.Code                           = \\u041A\\u043E\\u0434\nColumn.Commodity                      = \\u0411\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440\nColumn.CostBasis                      = \\u0411\\u0430\\u0437\\u043E\\u0432\\u0430\\u044F \\u0441\\u0442\\u043E\\u0438\\u043C\\u043E\\u0441\\u0442\\u044C\nColumn.Credit                         = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\nColumn.Currency                       = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430\nColumn.Date                           = \\u0414\\u0430\\u0442\\u0430\nColumn.Day                            = \\u0414\\u0435\\u043D\\u044C\nColumn.Debit                          = \\u0414\\u0435\\u0431\\u0438\\u0442\nColumn.Decrease                       = \\u0423\\u043C\\u0435\\u043D\\u044C\\u0448\\u0435\\u043D\\u0438\\u0435\nColumn.Deposit                        = \\u041F\\u043E\\u043F\\u043E\\u043B\\u043D\\u0435\\u043D\\u0438\\u0435\nColumn.Description                    = \\u041E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435\nColumn.Due                            = \\u0421\\u0440\\u043E\\u043A\nColumn.Enabled                        = \\u0410\\u043A\\u0442\\u0438\\u0432\\u043D\\u043E\nColumn.Entries                        = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nColumn.Event                          = \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u0435\nColumn.ExchangeRate                   = \\u041A\\u0443\\u0440\\u0441\nColumn.Expense                        = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\nColumn.Freq                           = \\u041F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0438\\u0447\\u043D\\u043E\\u0441\\u0442\\u044C\nColumn.Gain                           = \\u041F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C\nColumn.High                           = \\u041C\\u0430\\u043A\\u0441.\nColumn.Income                         = \\u041F\\u0440\\u0438\\u0445\\u043E\\u0434\nColumn.Increase                       = \\u0423\\u0432\\u0435\\u043B\\u0438\\u0447\\u0435\\u043D\\u0438\\u0435\nColumn.Investment                     = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u044F\nColumn.LastPosted                     = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0439 \\u0440\\u0430\\u0437\nColumn.Loss                           = \\u0423\\u0431\\u044B\\u0442\\u043E\\u043A\nColumn.Low                            = \\u041C\\u0438\\u043D.\nColumn.Memo                           = \\u0417\\u0430\\u043C\\u0435\\u0442\\u043A\\u0438\nColumn.MktValue                       = \\u0420\\u044B\\u043D\\u043E\\u0447\\u043D\\u0430\\u044F \\u0446\\u0435\\u043D\\u0430\nColumn.Month                          = \\u041C\\u0435\\u0441\\u044F\\u0446\nColumn.Num                            = \\u041D\\u043E\\u043C\\u0435\\u0440\nColumn.Payee                          = \\u041A\\u043E\\u043D\\u0442\\u0440\\u0430\\u0433\\u0435\\u043D\\u0442\nColumn.Payment                        = \\u041F\\u043B\\u0430\\u0442\\u0435\\u0436\nColumn.Percentile                     = \\u0414\\u043E\\u043B\\u044F\nColumn.Period                         = \\u041F\\u0435\\u0440\\u0438\\u043E\\u0434\nColumn.Price                          = \\u0426\\u0435\\u043D\\u0430\nColumn.Print                          = \\u041F\\u0435\\u0447\\u0430\\u0442\\u044C\nColumn.PropName                       = \\u0418\\u043C\\u044F \\u0441\\u0432\\u043E\\u0439\\u0441\\u0442\\u0432\\u0430\nColumn.PropVal                        = \\u0417\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u0435 \\u0441\\u0432\\u043E\\u0439\\u0441\\u0442\\u0432\\u0430\nColumn.Quantity                       = \\u041A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E\nColumn.Rebate                         = \\u0421\\u043A\\u0438\\u0434\\u043A\\u0430\nColumn.Receive                        = \\u041F\\u0440\\u0438\\u0445\\u043E\\u0434\nColumn.ReconciledBalance              = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u043D\\u044B\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\nColumn.Remaining                      = \\u041E\\u0441\\u0442\\u0430\\u0442\\u043E\\u043A\nColumn.Script                         = \\u0421\\u043A\\u0440\\u0438\\u043F\\u0442\nColumn.Security                       = \\u0426\\u0435\\u043D\\u043D\\u0430\\u044F \\u0431\\u0443\\u043C\\u0430\\u0433\\u0430\nColumn.Short.InternalRateOfReturn     = \\u0412\\u041D\\u0414(IRR)\nColumn.Short.PercentagePortfolio      = \\u041F\\u043E\\u0440\\u0442\\u0444\\u0435\\u043B\\u044C %\nColumn.Short.Quantity                 = \\u041A\\u043E\\u043B-\\u0432\\u043E\nColumn.Short.RealizedGain             = \\u0420\\u0435\\u0430\\u043B\\u0438\\u0437\\u043E\\u0432\\u0430\\u043D\\u043D\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C\nColumn.Short.RealizedGainPercentage   = \\u0420\\u0435\\u0430\\u043B\\u0438\\u0437\\u043E\\u0432\\u0430\\u043D\\u043D\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C %\nColumn.Short.TotalGain                = \\u041E\\u0431\\u0449\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C\nColumn.Short.TotalGainPercentage      = \\u041E\\u0431\\u0449\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C %\nColumn.Short.UnrealizedGain           = \\u041D\\u0435\\u0440\\u0435\\u0430\\u043B\\u0438\\u0437\\u043E\\u0432\\u0430\\u043D\\u043D\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C\nColumn.Short.UnrealizedGainPercentage = \\u041D\\u0435\\u0440\\u0435\\u0430\\u043B\\u0438\\u0437\\u043E\\u0432\\u0430\\u043D\\u043D\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C %\nColumn.Spend                          = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\nColumn.Timestamp                      = \\u041C\\u0435\\u0442\\u043A\\u0430 \\u0432\\u0440\\u0435\\u043C\\u0435\\u043D\\u0438\nColumn.Total                          = \\u0418\\u0442\\u043E\\u0433\\u043E\nColumn.TotalCostBasis                 = \\u0418\\u0442\\u043E\\u0433\\u043E \\u0431\\u0430\\u0437\\u043E\\u0432\\u0430\\u044F \\u0441\\u0442\\u043E\\u0438\\u043C\\u043E\\u0441\\u0442\\u044C\nColumn.Type                           = \\u0422\\u0438\\u043F\nColumn.Value                          = \\u0417\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u0435\nColumn.Volume                         = \\u041E\\u0431\\u044A\\u0435\\u043C\nColumn.Withdrawal                     = \\u0421\\u043D\\u044F\\u0442\\u0438\\u0435\n\nDataStoreType.Bxds = \\u0411\\u0438\\u043D\\u0430\\u0440\\u043D\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B\nDataStoreType.H2   = H2\nDataStoreType.HSQL = HyperSQL\nDataStoreType.XML  = XML \\u0444\\u0430\\u0439\\u043B\n\nItem.Amount         = \\u0421\\u0443\\u043C\\u043C\\u0430\nItem.CashDeposit    = \\u0412\\u043D\\u0435\\u0441\\u0435\\u043D\\u0438\\u0435 \\u043D\\u0430\\u043B\\u0438\\u0447\\u043D\\u043E\\u0441\\u0442\\u0438\nItem.CashWithdrawal = \\u0421\\u043D\\u044F\\u0442\\u0438\\u0435 \\u043D\\u0430\\u043B\\u0438\\u0447\\u043D\\u043E\\u0441\\u0442\\u0438\nItem.Date           = \\u0414\\u0430\\u0442\\u0430\nItem.EFT            = \\u042D\\u043B\\u0435\\u043A\\u0442\\u0440\\u043E\\u043D\\u043D\\u044B\\u0439 \\u043F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434\nItem.Memo           = \\u0417\\u0430\\u043C\\u0435\\u0442\\u043A\\u0438\nItem.NextNum        = \\u0421\\u043B\\u0435\\u0434\\u0443\\u044E\\u0449\\u0438\\u0439 \\u2116\nItem.Payee          = \\u041A\\u043E\\u043D\\u0442\\u0440\\u0430\\u0433\\u0435\\u043D\\u0442\nItem.Trans          = \\u041F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434\n\nLabel.AccentColor         = \\u0426\\u0432\\u0435\\u0442 \\u0430\\u043A\\u043A\\u0446\\u0435\\u043D\\u0442\\u0430:\nLabel.Account             = \\u0421\\u0447\\u0435\\u0442:\nLabel.AccountCode         = \\u041A\\u043E\\u0434 \\u0441\\u0447\\u0435\\u0442\\u0430:\nLabel.AccountNumber       = \\u041D\\u043E\\u043C\\u0435\\u0440 \\u0441\\u0447\\u0435\\u0442\\u0430:\nLabel.AccountOptions      = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u044B \\u0441\\u0447\\u0435\\u0442\\u0430:\nLabel.AccountSeparator    = \\u0420\\u0430\\u0437\\u0434\\u0435\\u043B\\u0438\\u0442\\u0435\\u043B\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430:\nLabel.AccountStatus       = \\u0421\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430:\nLabel.AccountType         = \\u0422\\u0438\\u043F \\u0441\\u0447\\u0435\\u0442\\u0430:\nLabel.Action              = \\u0414\\u0435\\u0439\\u0441\\u0442\\u0432\\u0438\\u0435:\nLabel.Amount              = \\u0421\\u0443\\u043C\\u043C\\u0430:\nLabel.AnIntRate           = \\u0413\\u043E\\u0434\\u043E\\u0432\\u0430\\u044F \\u0441\\u0442\\u0430\\u0432\\u043A\\u0430:\nLabel.Available           = \\u0414\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u043E:\nLabel.Balance             = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.BankAccount         = \\u0411\\u0430\\u043D\\u043A\\u043E\\u0432\\u0441\\u043A\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442:\nLabel.BankID              = \\u0418\\u0434\\u0435\\u043D\\u0442\\u0438\\u0444\\u0438\\u043A\\u0430\\u0442\\u043E\\u0440 \\u0431\\u0430\\u043D\\u043A\\u0430(\\u0411\\u0418\\u041A):\nLabel.BaseAccount         = \\u0411\\u0430\\u0437\\u043E\\u0432\\u044B\\u0439 \\u0441\\u0447\\u0435\\u0442:\nLabel.BaseColor           = \\u0411\\u0430\\u0437\\u043E\\u0432\\u044B\\u0439 \\u0446\\u0432\\u0435\\u0442:\nLabel.Bottom              = Bottom:\nLabel.By                  = \\u041F\\u043E:\nLabel.CashBalance         = \\u0414\\u0435\\u043D\\u0435\\u0436\\u043D\\u044B\\u0435 \\u0441\\u0440\\u0435\\u0434\\u0441\\u0442\\u0432\\u0430:\nLabel.Close               = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u0438\\u0435:\nLabel.Color=Color:\nLabel.Commodity           = \\u0411\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440:\nLabel.CompDaysPerYear     = \\u0414\\u043D\\u0435\\u0439 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0438\\u0437\\u0430\\u0446\\u0438\\u0438 \\u0432 \\u0433\\u043E\\u0434:\nLabel.CompPerTerm         = \\u041A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0438\\u0437\\u0430\\u0446\\u0438\\u0439 \\u0432 \\u0433\\u043E\\u0434:\nLabel.Compare             = \\u0421\\u0440\\u0430\\u0432\\u043D\\u0438\\u0442\\u044C:\nLabel.ConfirmPassword     = \\u041F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0438\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C:\nLabel.ConnTimeout         = \\u0422\\u0430\\u0439\\u043C\\u0430\\u0443\\u0442 \\u0441\\u043E\\u0435\\u0434\\u0438\\u043D\\u0435\\u043D\\u0438\\u044F:\nLabel.Count               = \\u041A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E:\nLabel.CreateCurr          = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443:\nLabel.CssFiles            = CSS \\u0444\\u0430\\u0439\\u043B\\u044B\nLabel.CsvFiles            = Csv \\u0444\\u0430\\u0439\\u043B\\u044B\nLabel.Curr/Comm           = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 / \\u0411\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440:\nLabel.Currencies          = \\u0412\\u0430\\u043B\\u044E\\u0442\\u044B:\nLabel.Currency            = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430:\nLabel.Current             = \\u0412\\u044B\\u0431\\u0440\\u0430\\u043D\\u043E:\nLabel.DatabaseBackend     = \\u0422\\u0438\\u043F \\u0411\\u0414:\nLabel.DatabaseName        = \\u0420\\u0430\\u0441\\u043F\\u043E\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435 \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445:\nLabel.DatabaseServer      = \\u0421\\u0435\\u0440\\u0432\\u0435\\u0440 \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445:\nLabel.Date                = \\u0414\\u0430\\u0442\\u0430:\nLabel.DateFormat          = \\u0424\\u043E\\u0440\\u043C\\u0430\\u0442 \\u0434\\u0430\\u0442\\u044B:\nLabel.DaysPastDue         = \\u041F\\u0440\\u043E\\u0441\\u0440\\u043E\\u0447\\u0435\\u043D\\u043E \\u0434\\u043D\\u0435\\u0439:\nLabel.DefaultCurrency     = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E:\nLabel.DelaySec            = \\u0417\\u0430\\u0434\\u0435\\u0440\\u0436\\u043A\\u0430 (\\u0441\\u0435\\u043A)\nLabel.Description         = \\u041E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435:\nLabel.DestAccount         = \\u0426\\u0435\\u043B\\u0435\\u0432\\u043E\\u0439 \\u0441\\u0447\\u0435\\u0442:\nLabel.Difference          = \\u0420\\u0430\\u0437\\u043D\\u0438\\u0446\\u0430:\nLabel.Dividend            = \\u0414\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434:\nLabel.EndDate             = \\u041A\\u043E\\u043D\\u0435\\u0447\\u043D\\u0430\\u044F \\u0434\\u0430\\u0442\\u0430:\nLabel.EndOn               = \\u0417\\u0430\\u043A\\u0430\\u043D\\u0447\\u0438\\u0432\\u0430\\u0435\\u0442\\u0441\\u044F:\nLabel.EndRow              = \\u041A\\u043E\\u043D\\u0435\\u0447\\u043D\\u0430\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430:\nLabel.EndingBalance       = \\u041A\\u043E\\u043D\\u0435\\u0447\\u043D\\u044B\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.EquityAccount       = \\u0421\\u0447\\u0435\\u0442 \\u0441\\u043E\\u0431\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0441\\u0440\\u0435\\u0434\\u0441\\u0442\\u0432:\nLabel.EscrowPmi           = \\u042D\\u0441\\u043A\\u0440\\u043E\\u0443, PMI, \\u0442.\\u0434.:\nLabel.Event               = \\u0421\\u043E\\u0431\\u044B\\u0442\\u0438\\u0435:\nLabel.Every               = \\u041A\\u0430\\u0436\\u0434\\u044B\\u0435:\nLabel.ExchangeAmount      = \\u041E\\u0431\\u043C\\u0435\\u043D \\u043D\\u0430:\nLabel.ExchangeRate        = \\u041A\\u0443\\u0440\\u0441 \\u043E\\u0431\\u043C\\u0435\\u043D\\u0430:\nLabel.ExchangedAmount     = \\u041A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E \\u043F\\u043E\\u0441\\u043B\\u0435 \\u043E\\u0431\\u043C\\u0435\\u043D\\u0430:\nLabel.Fees                = \\u041A\\u043E\\u043C\\u0438\\u0441\\u0441\\u0438\\u0438:\nLabel.FeesAccount         = \\u0421\\u0447\\u0435\\u0442 \\u043A\\u043E\\u043C\\u0438\\u0441\\u0441\\u0438\\u0439:\nLabel.FileName            = \\u0418\\u043C\\u044F \\u0444\\u0430\\u0439\\u043B\\u0430:\nLabel.FillAll             = \\u0417\\u0430\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C \\u0432\\u0441\\u0435:\nLabel.FirstPayDate        = \\u0414\\u0430\\u0442\\u0430 \\u043F\\u0435\\u0440\\u0432\\u043E\\u0433\\u043E \\u043F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0430:\nLabel.FocusColor          = \\u0426\\u0432\\u0435\\u0442 \\u0432\\u044B\\u0434\\u0435\\u043B\\u0435\\u043D\\u043D\\u043E\\u0433\\u043E:\nLabel.Format              = Format:\nLabel.Frequency           = \\u041F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0438\\u0447\\u043D\\u043E\\u0441\\u0442\\u044C:\nLabel.FullNumFormat       = \\u041F\\u043E\\u043B\\u043D\\u044B\\u0439 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442 \\u0447\\u0438\\u0441\\u043B\\u0430\nLabel.Gains               = \\u041F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = \\u0412\\u044B\\u0441\\u043E\\u0442\\u0430:\nLabel.High                = \\u041C\\u0430\\u043A\\u0441.:\nLabel.Host                = \\u0425\\u043E\\u0441\\u0442:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = \\u0421\\u0447\\u0435\\u0442 \\u0434\\u043E\\u0445\\u043E\\u0434\\u0430:\nLabel.InterestAccount     = \\u0421\\u0447\\u0435\\u0442 \\u043D\\u0430\\u0447\\u0438\\u0441\\u043B\\u0435\\u043D\\u043D\\u044B\\u0445 \\u043F\\u0440\\u043E\\u0446\\u0435\\u043D\\u0442\\u043E\\u0432:\nLabel.LastOccurrence      = \\u041F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0438\\u0439 \\u0440\\u0430\\u0437:\nLabel.Layout              = \\u0420\\u0430\\u0441\\u043F\\u043E\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435:\nLabel.Left                = Left:\nLabel.LoanTerm            = \\u0421\\u0440\\u043E\\u043A \\u0437\\u0430\\u0439\\u043C\\u0430 (\\u043C\\u0435\\u0441\\u044F\\u0446\\u0435\\u0432)\nLabel.Low                 = \\u041C\\u0438\\u043D.:\nLabel.MarketValue         = \\u0421\\u0442\\u043E\\u0438\\u043C\\u043E\\u0441\\u0442\\u044C \\u0426\\u0411:\nLabel.MaxBackupCount      = \\u041C\\u0430\\u043A\\u0441\\u0438\\u043C\\u0430\\u043B\\u044C\\u043D\\u043E\\u0435 \\u043A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u044B\\u0445 \\u043A\\u043E\\u043F\\u0438\\u0439:\nLabel.Memo                = \\u0417\\u0430\\u043C\\u0435\\u0442\\u043A\\u0438:\nLabel.Monospace           = \\u041C\\u043E\\u043D\\u043E\\u0448\\u0438\\u0440\\u0438\\u043D\\u043D\\u044B\\u0439:\nLabel.Name                = \\u041D\\u0430\\u0437\\u0432\\u0430\\u043D\\u0438\\u0435:\nLabel.NetIncome           = \\u0427\\u0438\\u0441\\u0442\\u044B\\u0439 \\u0434\\u043E\\u0445\\u043E\\u0434:\nLabel.NewPassword         = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C:\nLabel.NextPayDate         = \\u0414\\u0430\\u0442\\u0430 \\u0441\\u043B\\u0435\\u0434\\u0443\\u044E\\u0449\\u0435\\u0433\\u043E \\u043F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0430:\nLabel.Notes               = \\u041F\\u0440\\u0438\\u043C\\u0435\\u0447\\u0430\\u043D\\u0438\\u044F:\nLabel.NumTrans            = \\u041A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439:\nLabel.Number              = \\u041D\\u043E\\u043C\\u0435\\u0440:\nLabel.OfxFiles            = \\u0424\\u0430\\u0439\\u043B\\u044B Ofx\nLabel.OpenStateDate       = \\u0414\\u0430\\u0442\\u0430 \\u043D\\u0430\\u0447\\u0430\\u043B\\u044C\\u043D\\u043E\\u0433\\u043E \\u0441\\u043E\\u0441\\u0442\\u043E\\u044F\\u043D\\u0438\\u044F:\nLabel.OpeningBalance      = \\u041D\\u0430\\u0447\\u0430\\u043B\\u044C\\u043D\\u044B\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.OrigLoanAmt         = \\u041F\\u0435\\u0440\\u0432\\u043E\\u043D\\u0430\\u0447\\u0430\\u043B\\u044C\\u043D\\u0430\\u044F \\u0441\\u0443\\u043C\\u043C\\u0430 \\u0437\\u0430\\u0439\\u043C\\u0430:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = \\u041F\\u0430\\u0440\\u043E\\u043B\\u044C:\nLabel.Path                = \\u041F\\u0443\\u0442\\u044C:\nLabel.Pattern             = \\u0428\\u0430\\u0431\\u043B\\u043E\\u043D:\nLabel.PayPerTerm          = \\u041F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0435\\u0439 \\u0432 \\u0433\\u043E\\u0434:\nLabel.Payee               = \\u041A\\u043E\\u043D\\u0442\\u0440\\u0430\\u0433\\u0435\\u043D\\u0442:\nLabel.Period              = \\u041F\\u0435\\u0440\\u0438\\u043E\\u0434:\nLabel.Port                = \\u041F\\u043E\\u0440\\u0442:\nLabel.Prefix              = \\u041F\\u0440\\u0435\\u0444\\u0438\\u043A\\u0441:\nLabel.Price               = \\u0426\\u0435\\u043D\\u0430:\nLabel.Proportional        = \\u041F\\u0440\\u043E\\u043F\\u043E\\u0440\\u0446\\u0438\\u043E\\u043D\\u0430\\u043B\\u044C\\u043D\\u044B\\u0439:\nLabel.Quantity            = \\u041A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E:\nLabel.QuoteSource         = \\u0418\\u0441\\u0442\\u043E\\u0447\\u043D\\u0438\\u043A \\u043A\\u043E\\u0442\\u0438\\u0440\\u043E\\u0432\\u043E\\u043A:\nLabel.ReceivingAccount    = \\u0421\\u0447\\u0435\\u0442-\\u043F\\u043E\\u043B\\u0443\\u0447\\u0430\\u0442\\u0435\\u043B\\u044C:\nLabel.ReconciledBalance   = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u043D\\u044B\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.RemindLater         = \\u041D\\u0430\\u043F\\u043E\\u043C\\u043D\\u0438\\u0442\\u044C \\u043F\\u043E\\u0441\\u043B\\u0435:\nLabel.RenameBudget        = \\u041F\\u0435\\u0440\\u0435\\u0438\\u043C\\u0435\\u043D\\u043E\\u0432\\u0430\\u0442\\u044C \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442:\nLabel.RepeatOn            = \\u041F\\u043E\\u0432\\u0442\\u043E\\u0440\\u044F\\u0442\\u044C:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u043A\\u043E\\u0442\\u0438\\u0440\\u043E\\u0432\\u043A\\u0438:\nLabel.Resolution          = \\u041F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0438\\u0447\\u043D\\u043E\\u0441\\u0442\\u044C:\nLabel.ReturnOfCapital     = \\u0412\\u043E\\u0437\\u0432\\u0440\\u0430\\u0442 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0430:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = \\u0422\\u043E\\u0447\\u043D\\u043E\\u0441\\u0442\\u044C:\nLabel.Securities          = \\u0426\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438:\nLabel.Security            = \\u0426\\u0435\\u043D\\u043D\\u0430\\u044F \\u0431\\u0443\\u043C\\u0430\\u0433\\u0430:\nLabel.ShortNumFormat      = \\u041A\\u0440\\u0430\\u0442\\u043A\\u0438\\u0439 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442 \\u0447\\u0438\\u0441\\u043B\\u0430\nLabel.ShowEmptyAccounts   = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u043F\\u0443\\u0441\\u0442\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nLabel.SortOrder           = \\u041F\\u043E\\u0440\\u044F\\u0434\\u043E\\u043A \\u0441\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u043A\\u0438:\nLabel.SpreadsheetFiles    = \\u0424\\u0430\\u0439\\u043B\\u044B \\u0442\\u0430\\u0431\\u043B\\u0438\\u0446\nLabel.StartDate           = \\u041D\\u0430\\u0447\\u0430\\u043B\\u044C\\u043D\\u0430\\u044F \\u0434\\u0430\\u0442\\u0430:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = \\u041D\\u0430\\u0447\\u0430\\u043B\\u044C\\u043D\\u0430\\u044F \\u043F\\u043E\\u0437\\u0438\\u0446\\u0438\\u044F:\nLabel.StartRow            = \\u041D\\u0430\\u0447\\u0430\\u043B\\u044C\\u043D\\u0430\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430:\nLabel.StatementDate       = \\u0414\\u0430\\u0442\\u0430 \\u0443\\u0442\\u0432\\u0435\\u0440\\u0436\\u0434\\u0435\\u043D\\u0438\\u044F:\nLabel.StorageType         = \\u0422\\u0438\\u043F \\u0445\\u0440\\u0430\\u043D\\u0438\\u043B\\u0438\\u0449\\u0430:\nLabel.Suffix              = \\u0421\\u0443\\u0444\\u0444\\u0438\\u043A\\u0441:\nLabel.Symbol              = \\u0421\\u0438\\u043C\\u0432\\u043E\\u043B:\nLabel.TargetBalance       = \\u0426\\u0435\\u043B\\u0435\\u0432\\u043E\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.Top                 = Top:\nLabel.Total               = \\u0418\\u0442\\u043E\\u0433\\u043E:\nLabel.Transaction         = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F:\nLabel.TransferFrom        = \\u041F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434 \\u0441:\nLabel.TransferTo          = \\u041F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434 \\u043D\\u0430:\nLabel.Type                = \\u0422\\u0438\\u043F:\nLabel.Units               = Units:\nLabel.UserName            = \\u0418\\u043C\\u044F \\u043F\\u043E\\u043B\\u044C\\u0437\\u043E\\u0432\\u0430\\u0442\\u0435\\u043B\\u044F:\nLabel.Value               = \\u0417\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u0435:\nLabel.Verify              = \\u041F\\u0440\\u043E\\u0432\\u0435\\u0440\\u043A\\u0430:\nLabel.VerifyPassword      = \\u041F\\u0440\\u043E\\u0432\\u0435\\u0440\\u043A\\u0430 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044F:\nLabel.Visible             = \\u0412\\u0438\\u0434\\u0438\\u043C\\u044B\\u0439:\nLabel.Volume              = \\u041E\\u0431\\u044A\\u0435\\u043C:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML \\u0444\\u0430\\u0439\\u043B\\u044B\nLabel.Year                = \\u0413\\u043E\\u0434:\nLabel.jGnashFiles         = \\u0444\\u0430\\u0439\\u043B\\u044B jGnash\n\nMenu.About.Name                       = \\u041E _\\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u0435...\nMenu.About.Tooltip                    = \\u0418\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F \\u043E jGnash\nMenu.Account.Name                     = \\u0421\\u0447\\u0435\\u0442\nMenu.AccountRegister.Name             = \\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439...\nMenu.AddRemoveCurrency.Name=_\\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C/\\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0432\\u0430\\u043B\\u044E\\u0442\nMenu.BackgroundCurrencyUpdate.Tooltip = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0432\\u0441\\u0435\\u0445 \\u043A\\u0443\\u0440\\u0441\\u043E\\u0432 \\u0432 \\u0444\\u043E\\u043D\\u0435\nMenu.BackgroundSecurityUpdate.Name    = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0446\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0431\\u0443\\u043C\\u0430\\u0433\nMenu.BackgroundSecurityUpdate.Tooltip = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0441\\u0442\\u043E\\u0438\\u043C\\u043E\\u0441\\u0442\\u0438 \\u0446\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0431\\u0443\\u043C\\u0430\\u0433 \\u0432 \\u0444\\u043E\\u043D\\u0435\nMenu.BalanceSheet.Name                = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u0432\\u044B\\u0439 \\u043E\\u0442\\u0447\\u0435\\u0442...\nMenu.BaseColor.Name                   = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0431\\u0430\\u0437\\u043E\\u0432\\u044B\\u0435 \\u0446\\u0432\\u0435\\u0442\\u0430\\u2026\nMenu.BudgetManager.Name               = \\u041C\\u0435\\u043D\\u0435\\u0434\\u0436\\u0435\\u0440 _\\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0430...\nMenu.ChangeCredentials.Name           = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445...\nMenu.ChangeCredentials.Tooltip        = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445\nMenu.Charts.Name                      = \\u0414\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u044B\nMenu.Cleared.Name                     = \\u041E\\u0447\\u0438\\u0449\\u0435\\u043D\\u043E\nMenu.Close.Name                       = _\\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C\nMenu.Close.Tooltip                    = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C \\u0430\\u043A\\u0442\\u0438\\u0432\\u043D\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B\nMenu.CloseAllWindows.Name             = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0442\\u044C \\u0432\\u0441\\u0435 \\u043E\\u043A\\u043D\\u0430\nMenu.ConfigImportFilters.Name         = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0438\\u0442\\u044C \\u0444\\u0438\\u043B\\u044C\\u0442\\u0440\\u044B \\u0438\\u043C\\u043F\\u043E\\u0440\\u0442\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439...\nMenu.Console.Name                     = \\u041E\\u043A\\u043D\\u043E Java-\\u043A\\u043E\\u043D\\u0441\\u043E\\u043B\\u0438...\nMenu.Copy.Name                        = _\\u041A\\u043E\\u043F\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\nMenu.Copy.Tooltip                     = \\u0414\\u0443\\u0431\\u043B\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0432\\u044B\\u0434\\u0435\\u043B\\u0435\\u043D\\u043D\\u044B\\u0439 \\u044D\\u043B\\u0435\\u043C\\u0435\\u043D\\u0442\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _\\u0412\\u0430\\u043B\\u044E\\u0442\\u044B\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = \\u041F\\u043E _\\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E...\nMenu.DefaultCurrency.Tooltip          = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nMenu.Delete.Name                      = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\nMenu.Duplicate.Name                   = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043A\\u043E\\u043F\\u0438\\u044E\nMenu.Edit.Name                        = _\\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C\nMenu.EditTranNumList.Name             = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0441\\u043F\\u0438\\u0441\\u043E\\u043A \\u043D\\u043E\\u043C\\u0435\\u0440\\u043E\\u0432 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nMenu.Exit.Name                        = _\\u0412\\u044B\\u0445\\u043E\\u0434\nMenu.Exit.Tooltip                     = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u044C \\u0440\\u0430\\u0431\\u043E\\u0442\\u0443 \\u0441 jGnash\nMenu.Export.Name                      = _\\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\nMenu.ExportAccounts.Name              = \\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442 \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432...\nMenu.File.Archive                     = \\u0410\\u0440\\u0445\\u0438\\u0432...\nMenu.File.Name                        = _\\u0424\\u0430\\u0439\\u043B\nMenu.Filter.Name                      = \\u0424_\\u0438\\u043B\\u044C\\u0442\\u0440\\u044B\nMenu.FontSize.Name                    = \\u0420\\u0430\\u0437\\u043C\\u0435\\u0440 \\u0448\\u0440\\u0438\\u0444\\u0442\\u0430\\u2026\nMenu.Help.Name                        = _\\u041F\\u043E\\u043C\\u043E\\u0449\\u044C\nMenu.Hide.Name                        = \\u0421\\u043A\\u0440\\u044B\\u0442\\u044C\nMenu.HistoryChart.Name                = \\u0414\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u0430 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438...\nMenu.HistoryCommodity.Name            = _\\u0418\\u0441\\u0442\\u043E\\u0440\\u0438\\u044F...\nMenu.HistoryImport.Name               = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438...\nMenu.IEBarChart.Name                  = \\u0414\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u044B/\\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B\"...\nMenu.IEPieChart.Name                  = \\u041A\\u0440\\u0443\\u0433\\u043E\\u0432\\u0430\\u044F \\u0434\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u044B / \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B\"...\nMenu.Import.Name                      = \\u0418\\u043C_\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\nMenu.ImportAccounts.Name              = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430...\nMenu.ImportAccounts.Tooltip           = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nMenu.ImportJgnash.Name                = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 jGnash...\nMenu.ImportJgnash.Tooltip             = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0444\\u0430\\u0439\\u043B\\u043E\\u0432 jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0444\\u0430\\u0439\\u043B\\u043E\\u0432 SWIFT MT940\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0444\\u0430\\u0439\\u043B\\u043E\\u0432 OFX/QFX\nMenu.ImportQif.Name                   = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C _QIF...\nMenu.Jump.Name                        = \\u041F\\u0435\\u0440\\u0435\\u0439\\u0442\\u0438\nMenu.ListOfAccounts.Name              = _\\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\\u2026\nMenu.Locale.Name                      = \\u042F\\u0437\\u044B\\u043A \\u0438\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0430...\nMenu.LookAndFeel.Name                 = \\u0412\\u043D\\u0435\\u0448\\u043D\\u0438\\u0439 \\u0432\\u0438\\u0434 (Look & Feel)\nMenu.MarkAs.Name                      = \\u041F\\u043E\\u043C\\u0435\\u0442\\u0438\\u0442\\u044C \\u043A\\u0430\\u043A\nMenu.Modify.Name                      = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C\nMenu.ModifyCommodity.Name             = _\\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C / \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C...\nMenu.ModifyCurrency.Name              = _\\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C...\nMenu.ModifyExchangeRates.Name         = _\\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043A\\u0443\\u0440\\u0441\\u044B \\u043E\\u0431\\u043C\\u0435\\u043D\\u0430...\nMenu.MonthBalance.Name                = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0437\\u0430 \\u043C\\u0435\\u0441\\u044F\\u0446 (\\u0434\\u0432\\u0438\\u0436\\u0435\\u043D\\u0438\\u0435 \\u043F\\u043E \\u0441\\u0447\\u0435\\u0442\\u0443)...\nMenu.MonthBalanceCompare.Name         = \\u0421\\u0440\\u0430\\u0432\\u043D\\u0435\\u043D\\u0438\\u0435 \\u043F\\u043E \\u043C\\u0435\\u0441\\u044F\\u0446\\u0430\\u043C\nMenu.MonthEndBalance.Name             = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u043D\\u0430 \\u043A\\u043E\\u043D\\u0435\\u0446 \\u043C\\u0435\\u0441\\u044F\\u0446\\u0430 (\\u043D\\u0430\\u0440\\u0430\\u0441\\u0442\\u0430\\u044E\\u0449\\u0438\\u043C \\u0438\\u0442\\u043E\\u0433\\u043E\\u043C)...\nMenu.MonthEndBalanceCSV.Name          = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u043D\\u0430 \\u043A\\u043E\\u043D\\u0435\\u0446 \\u043C\\u0435\\u0441\\u044F\\u0446\\u0430 (\\u043D\\u0430\\u0440\\u0430\\u0441\\u0442\\u0430\\u044E\\u0449\\u0438\\u043C \\u0438\\u0442\\u043E\\u0433\\u043E\\u043C) (CSV)...\nMenu.NetWorth.Name                    = \\u0421\\u043E\\u0431\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0439 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B...\nMenu.New.Name                         = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C _\\u043D\\u043E\\u0432\\u044B\\u0439\\u2026\nMenu.New.Tooltip                      = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043D\\u043E\\u0432\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445\nMenu.NewReminder.Name                 = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435\nMenu.Open.Name                        = _\\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C\\u2026\nMenu.Open.Tooltip                     = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B\nMenu.Option.Name                      = _\\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438...\nMenu.Option.Tooltip                   = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u044B\nMenu.PackDatabase.Name                = \\u0421\\u0436\\u0430\\u0442\\u044C \\u0431\\u0430\\u0437\\u0443 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445...\nMenu.PayeePieChart.Name               = \\u041A\\u0440\\u0443\\u0433\\u043E\\u0432\\u0430\\u044F \\u0434\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u044B/\\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B \\u043F\\u043E \\u043A\\u043E\\u043D\\u0442\\u0440\\u0430\\u0433\\u0435\\u043D\\u0442\\u0443\"...\nMenu.PeriodicAccountBalance.Name      = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0441\\u0447\\u0435\\u0442\\u0430 \\u043F\\u043E \\u043F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0430\\u043C ...\nMenu.Portfolio.Name                   = \\u041F\\u043E\\u0440\\u0442\\u0444\\u0435\\u043B\\u044C...\nMenu.ProfitLoss.Name                  = \\u041F\\u0440\\u0438\\u0431\\u044B\\u043B\\u0438 \\u0438 \\u0443\\u0431\\u044B\\u0442\\u043A\\u0438...\nMenu.ProfitLossTXT.Name               = \\u041F\\u0440\\u0438\\u0431\\u044B\\u043B\\u0438 \\u0438 \\u0443\\u0431\\u044B\\u0442\\u043A\\u0438 (\\u0442\\u0435\\u043A\\u0441\\u0442)...\nMenu.Reconcile.Name                   = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u0442\\u044C\nMenu.Reconciled.Name                  = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u043D\\u043E\nMenu.RecurringList.Name               = _\\u041D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F...\nMenu.Register.Name                    = _\\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439...\nMenu.Reports.Name                     = _\\u041E\\u0442\\u0447\\u0435\\u0442\\u044B\nMenu.RunJavaScript.Name               = \\u0412\\u044B\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C JavaScript...\nMenu.RunJavaScript.Tooltip            = \\u0412\\u044B\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C JavaScript\nMenu.Save.Name                        = _\\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C\nMenu.Save.Tooltip                     = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0442\\u0435\\u043A\\u0443\\u0449\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B\nMenu.SaveAs.Name                      = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C _\\u043A\\u0430\\u043A...\nMenu.SaveAs.Tooltip                   = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0442\\u0435\\u043A\\u0443\\u0449\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B \\u043F\\u043E\\u0434 \\u0434\\u0440\\u0443\\u0433\\u0438\\u043C \\u0438\\u043C\\u0435\\u043D\\u0435\\u043C\nMenu.Securities.Name                  = _\\u0426\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438\nMenu.SetPassword.Name                 = \\u041D\\u0430\\u0437\\u043D\\u0430\\u0447\\u0438\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C...\nMenu.Show.Name                        = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C\nMenu.ShutdownServer.Name              = \\u0412\\u044B\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440...\nMenu.ShutdownServer.Tooltip           = \\u0412\\u044B\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = \\u0422\\u0435\\u043C\\u044B\nMenu.Tools.Name                       = _\\u0418\\u043D\\u0441\\u0442\\u0440\\u0443\\u043C\\u0435\\u043D\\u0442\\u044B\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = \\u041D\\u0435\\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u043D\\u043E\nMenu.View.Name                        = _\\u0412\\u0438\\u0434\nMenu.Window.Name                      = _\\u041E\\u043A\\u043D\\u043E\n\nMessage.AcceptLicense                = \\u042F \\u043F\\u0440\\u043E\\u0447\\u0438\\u0442\\u0430\\u043B(\\u0430), \\u043F\\u043E\\u043D\\u044F\\u043B(\\u0430) \\u0438 \\u043F\\u0440\\u0438\\u043D\\u0438\\u043C\\u0430\\u044E \\u0443\\u0441\\u043B\\u043E\\u0432\\u0438\\u044F \\u043B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u0438.\nMessage.AccountAdd                   = \\u0421\\u0447\\u0435\\u0442 \\u0434\\u043E\\u0431\\u0430\\u0432\\u043B\\u0435\\u043D\nMessage.AccountCode                  = \\u041A\\u043E\\u0434 \\u0441\\u0447\\u0435\\u0442\\u0430 \\u043D\\u0435 \\u0443\\u043D\\u0438\\u043A\\u0430\\u043B\\u0435\\u043D (\\u0441\\u0447\\u0435\\u0442 \\u0441 \\u0442\\u0430\\u043A\\u0438\\u043C \\u043A\\u043E\\u0434\\u043E\\u043C \\u0443\\u0436\\u0435 \\u0435\\u0441\\u0442\\u044C), \\u043F\\u043E\\u044D\\u0442\\u043E\\u043C\\u0443 \\u043A\\u043E\\u0434 \\u043D\\u0435 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\nMessage.AccountLocked                = \\u0421\\u0447\\u0435\\u0442 \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\nMessage.AccountModify                = \\u0421\\u0447\\u0435\\u0442 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\nMessage.AccountMoveFailed            = \\u041D\\u0435 \\u043C\\u043E\\u0433\\u0443 \\u043F\\u0435\\u0440\\u0435\\u043D\\u0435\\u0441\\u0442\\u0438 \\u0441\\u0447\\u0435\\u0442\nMessage.AccountRemove                = \\u0421\\u0447\\u0435\\u0442 \\u0443\\u0434\\u0430\\u043B\\u0435\\u043D\nMessage.AntiAlias                    = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u0438\\u0435 \\u0441\\u0433\\u043B\\u0430\\u0436\\u0438\\u0432\\u0430\\u043D\\u0438\\u044F \\u0442\\u0435\\u043A\\u0441\\u0442\\u0430!\nMessage.AutoSaveOff                  = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0432\\u044B\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u0430\nMessage.AutoSaveOn                   = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u0435\\u0441\\u043A\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0432\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u0430\nMessage.CSVFile                      = \\u0424\\u0430\\u0439\\u043B\\u044B \\u0440\\u0430\\u0437\\u0434\\u0435\\u043B\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0437\\u0430\\u043F\\u044F\\u0442\\u044B\\u043C\\u0438 (*.CSV)\nMessage.CheckRecurring               = \\u041F\\u0440\\u043E\\u0432\\u0435\\u0440\\u043A\\u0430 \\u043F\\u043E\\u0432\\u0442\\u043E\\u0440\\u044F\\u044E\\u0449\\u0438\\u0445\\u0441\\u044F \\u043F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0435\\u0439\nMessage.ClosingFile                  = \\u0417\\u0430\\u043A\\u0440\\u044B\\u0432\\u0430\\u044E \\u0444\\u0430\\u0439\\u043B\nMessage.CollectingReportData         = \\u041F\\u043E\\u0434\\u0433\\u043E\\u0442\\u043E\\u0432\\u043A\\u0430 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\nMessage.CompilingReport              = \\u0421\\u043E\\u0441\\u0442\\u0430\\u0432\\u043B\\u0435\\u043D\\u0438\\u0435 \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430...\nMessage.Confirm.ExecuteReminder      = \\u0412\\u044B\\u043F\\u043E\\u043B\\u043D\\u0438\\u0442\\u044C \\u0441\\u0435\\u0439\\u0447\\u0430\\u0441?\nMessage.ConfirmBudgetDelete          = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u044B\\u0439 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442?\nMessage.ConfirmMultipleBudgetDelete  = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u044B\\u0435 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u044B?\nMessage.ConfirmMultipleTransDelete   = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u044B\\u0435 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438?\nMessage.ConfirmReminderDelete        = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u043E\\u0435 \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435?\nMessage.ConfirmSecurityHistoryDelete = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u044E \\u043F\\u043E \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u043E\\u0439 \\u0446\\u0435\\u043D\\u043D\\u043E\\u0439 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0435?\\n\\n\\u0423\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438 \\u0431\\u0443\\u0434\\u0435\\u0442 \\u043F\\u0440\\u043E\\u0438\\u0437\\u0432\\u0435\\u0434\\u0435\\u043D\\u043E \\u0432 \\u0444\\u043E\\u043D\\u0435 \\u0438 \\u043C\\u043E\\u0436\\u0435\\u0442 \\u0437\\u0430\\u043D\\u044F\\u0442\\u044C \\u043D\\u0435\\u043A\\u043E\\u0442\\u043E\\u0440\\u043E\\u0435 \\u0432\\u0440\\u0435\\u043C\\u044F.\nMessage.ConfirmTransDelete           = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u043D\\u0443\\u044E \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044E?\nMessage.CredentialChange             = \\u0423\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u044B\nMessage.CurrChange                   = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0430 \\u043D\\u0430:\nMessage.DownloadingX                 = \\u0417\\u0430\\u0433\\u0440\\u0443\\u0437\\u043A\\u0430 {0}\nMessage.EngineStart                  = \\u0411\\u0430\\u0437\\u0430 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u0438\\u043D\\u0438\\u0446\\u0438\\u0430\\u043B\\u0438\\u0437\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u0430\nMessage.EnterNetworkAuth             = \\u0412\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0435\\u0442\\u0435\\u0432\\u043E\\u0439 \\u0430\\u0443\\u0442\\u0435\\u043D\\u0442\\u0438\\u0444\\u0438\\u043A\\u0430\\u0446\\u0438\\u0438\nMessage.Error.AccountCreate          = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0441\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\nMessage.Error.AccountRemove          = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\\u0441\\u0447\\u0435\\u0442\nMessage.Error.AccountUpdate          = \\u041F\\u0440\\u0438 \\u043E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u0438 \\u0441\\u0447\\u0435\\u0442\\u0430 \\u0432\\u043E\\u0437\\u043D\\u0438\\u043A\\u043B\\u0430 \\u043E\\u0448\\u0438\\u0431\\u043A\\u0430\nMessage.Error.AddCommodity           = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0434\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u0431\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440\nMessage.Error.AddCurrency            = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0434\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443\nMessage.Error.AmortizationSave       = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0441\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u043D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u0430\\u043C\\u043E\\u0440\\u0442\\u0438\\u0437\\u0430\\u0446\\u0438\\u0438\nMessage.Error.BudgetDuplicate        = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0434\\u0443\\u0431\\u043B\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nMessage.Error.BudgetRemove           = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nMessage.Error.CreateBasicAccounts    = \\u0421\\u043D\\u0430\\u0447\\u0430\\u043B\\u0430 \\u0441\\u043E\\u0437\\u0434\\u0430\\u0439\\u0442\\u0435 \\u0431\\u0430\\u0437\\u043E\\u0432\\u044B\\u0439 \\u0441\\u0447\\u0435\\u0442\nMessage.Error.CredentialChange       = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0438\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0443\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u043E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435 {0}\nMessage.Error.DeleteExistingFile     = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0441\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0443\\u044E\\u0449\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B {0}\nMessage.Error.Duplicate              = \\u0414\\u0443\\u0431\\u043B\\u0438\\u043A\\u0430\\u0442\\u044B \\u043D\\u0435 \\u0440\\u0430\\u0437\\u0440\\u0435\\u0448\\u0435\\u043D\\u044B\nMessage.Error.EmptyKey               = \\u041A\\u043B\\u044E\\u0447 \\u0430\\u0442\\u0442\\u0440\\u0438\\u0431\\u0443\\u0442\\u0430 \\u043D\\u0435 \\u043C\\u043E\\u0436\\u0435\\u0442 \\u0431\\u044B\\u0442\\u044C \\u043F\\u0443\\u0441\\u0442\\u044B\\u043C \\u0438\\u043B\\u0438 null\nMessage.Error.FileNotFound           = \\u0424\\u0430\\u0439\\u043B \\u043D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\nMessage.Error.HistRemoval            = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u0432 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438 {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = \\u041E\\u0448\\u0438\\u0431\\u043A\\u0430 \\u0432\\u0432\\u043E\\u0434\\u0430-\\u0432\\u044B\\u0432\\u043E\\u0434\\u0430\nMessage.Error.InvalidAccountGroup    = \\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u0430\\u044F \\u0433\\u0440\\u0443\\u043F\\u043F\\u0430 \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nMessage.Error.InvalidTransactionTag  = \\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0439 \\u0442\\u0435\\u0433 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nMessage.Error.InvalidTransactionType = \\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0439 \\u0442\\u0438\\u043F \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nMessage.Error.InvalidUserPass        = \\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u044B\\u0439 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C \\u0438\\u043B\\u0438 \\u043F\\u043E\\u043F\\u044B\\u0442\\u043A\\u0430 \\u043E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B \\u043D\\u0435\\u0432\\u0435\\u0440\\u043D\\u043E\\u0433\\u043E \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442\\u0430\nMessage.Error.License                = \\u0423\\u0441\\u043B\\u043E\\u0432\\u0438\\u044F \\u043B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u0438 \\u043D\\u0435 \\u0431\\u044B\\u043B\\u0438 \\u043F\\u0440\\u0438\\u043D\\u044F\\u0442\\u044B\nMessage.Error.LoadingFile            = \\u041E\\u0448\\u0438\\u0431\\u043A\\u0430 \\u0437\\u0430\\u0433\\u0440\\u0443\\u0437\\u043A\\u0438 \\u0444\\u0430\\u0439\\u043B\\u0430\nMessage.Error.LogFileHandler         = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0443\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u043E\\u0431\\u0440\\u0430\\u0431\\u043E\\u0442\\u0447\\u0438\\u043A \\u0444\\u0430\\u0439\\u043B\\u0430 \\u043B\\u043E\\u0433\\u0430\nMessage.Error.MissingAttachment      = \\u0412\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435 \"{0}\" \\u043D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E\nMessage.Error.ModifyCommodity        = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0438\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0431\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440\nMessage.Error.ModifyCurrency         = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0438\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443\nMessage.Error.MoveAccount            = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u043F\\u0435\\u0440\\u0435\\u043C\\u0435\\u0441\\u0442\\u0438\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\nMessage.Error.NewBudget              = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0441\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043D\\u043E\\u0432\\u044B\\u0439 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nMessage.Error.ParseTransactions      = \\u041D\\u0435 \\u0440\\u0430\\u0441\\u043F\\u043E\\u0437\\u043D\\u0430\\u043D\\u0430 \\u043D\\u0438 \\u043E\\u0434\\u043D\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F\nMessage.Error.PasswordMatch          = \\u041F\\u0430\\u0440\\u043E\\u043B\\u0438 \\u043D\\u0435 \\u0441\\u043E\\u0432\\u043F\\u0430\\u0434\\u0430\\u044E\\u0442\nMessage.Error.ReminderAdd            = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0434\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435\nMessage.Error.ReminderUpdate         = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u043E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435\nMessage.Error.RemoveTempFile         = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u0440\\u0435\\u043C\\u0435\\u043D\\u043D\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B\nMessage.Error.SecurityAccountRemove  = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0446\\u0435\\u043D\\u043D\\u0443\\u044E \\u0431\\u0443\\u043C\\u0430\\u0433\\u0443 {0} \\u0438\\u0437 \\u0441\\u0447\\u0435\\u0442\\u0430 {1}\nMessage.Error.SecurityAccountUpdate  = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u043E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435 \\u0446\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0431\\u0443\\u043C\\u0430\\u0433 \\u0441\\u0447\\u0435\\u0442\\u0430\nMessage.Error.SecurityAdd            = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0434\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u0446\\u0435\\u043D\\u043D\\u0443\\u044E \\u0431\\u0443\\u043C\\u0430\\u0433\\u0443 {0}\nMessage.Error.SecurityUpdate         = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u043E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u0446\\u0435\\u043D\\u043D\\u0443\\u044E \\u0431\\u0443\\u043C\\u0430\\u0433\\u0443 {0}\nMessage.Error.ServerConnection       = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0443\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u0438\\u0442\\u044C \\u0441\\u043E\\u0435\\u0434\\u0438\\u043D\\u0435\\u043D\\u0438\\u0435 \\u0441 \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440\\u043E\\u043C\nMessage.Error.TranAddFail            = \\u041F\\u0440\\u0438 \\u0434\\u043E\\u0431\\u0430\\u0432\\u043B\\u0435\\u043D\\u0438\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438 \\u0432\\u043E\\u0437\\u043D\\u0438\\u043A\\u043B\\u0430 \\u0432\\u043D\\u0443\\u0442\\u0440\\u0435\\u043D\\u043D\\u044F\\u044F \\u043E\\u0448\\u0438\\u0431\\u043A\\u0430\nMessage.Error.TransferAttachment     = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u043F\\u0435\\u0440\\u0435\\u043C\\u0435\\u0441\\u0442\\u0438\\u0442\\u044C \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435 {0}\nMessage.Error.UnsupportedFileType    = \\u041D\\u0435\\u043F\\u043E\\u0434\\u0434\\u0435\\u0440\\u0436\\u0438\\u0432\\u0430\\u0435\\u043C\\u044B\\u0439 \\u0442\\u0438\\u043F \\u0444\\u0430\\u0439\\u043B\\u0430\nMessage.FileClosed                   = \\u0424\\u0430\\u0439\\u043B \\u0437\\u0430\\u043A\\u0440\\u044B\\u0442\nMessage.FileIsLocked                 = \\u0424\\u0430\\u0439\\u043B \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D \\u0434\\u0440\\u0443\\u0433\\u0438\\u043C \\u043F\\u0440\\u0438\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435\\u043C\nMessage.FileLoadComplete             = \\u0417\\u0430\\u0433\\u0440\\u0443\\u0437\\u043A\\u0430 \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0430\nMessage.FileNotValid                 = \\u0412\\u044B\\u0431\\u0440\\u0430\\u043D\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B \\u043D\\u0435 \\u043A\\u043E\\u0440\\u0440\\u0435\\u043A\\u0442\\u0435\\u043D\nMessage.FileSaveComplete             = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0435\\u043D\\u0438\\u0435 \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043E\nMessage.ImportWait                   = \\u041F\\u043E\\u0434\\u043E\\u0436\\u0434\\u0438\\u0442\\u0435, \\u0438\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0437\\u0430\\u0439\\u043C\\u0435\\u0442 \\u043D\\u0435\\u043A\\u043E\\u0442\\u043E\\u0440\\u043E\\u0435 \\u0432\\u0440\\u0435\\u043C\\u044F\nMessage.Info.LongUpgrade             = \\u0412\\u0430\\u0448 \\u0444\\u0430\\u0439\\u043B \\u0431\\u0443\\u0434\\u0435\\u0442 \\u043F\\u0440\\u0435\\u043E\\u0431\\u0440\\u0430\\u0437\\u043E\\u0432\\u0430\\u043D \\u0432 \\u043D\\u043E\\u0432\\u044B\\u0439 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442. \\u042D\\u0442\\u043E \\u043C\\u043E\\u0436\\u0435\\u0442 \\u0437\\u0430\\u043D\\u044F\\u0442\\u044C \\u043D\\u0435\\u043A\\u043E\\u0442\\u043E\\u0440\\u043E\\u0435 \\u0432\\u0440\\u0435\\u043C\\u044F.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = \\u0412\\u0430\\u0448 \\u0444\\u0430\\u0439\\u043B \\u0431\\u044B\\u043B \\u043F\\u0440\\u0435\\u043E\\u0431\\u0440\\u0430\\u0437\\u043E\\u0432\\u0430\\u043D \\u0432 \\u043D\\u043E\\u0432\\u044B\\u0439 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442.\\n\\u041E\\u0440\\u0438\\u0433\\u0438\\u043D\\u0430\\u043B\\u044C\\u043D\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B \\u0431\\u044B\\u043B \\u0441\\u043E\\u0445\\u0440\\u0430\\u043D\\u0435\\u043D \\u043A\\u0430\\u043A \"{0}\".\nMessage.JVM11                        = \\u0414\\u043B\\u044F \\u0440\\u0430\\u0431\\u043E\\u0442\\u044B jGnash \\u043D\\u0435\\u043E\\u0431\\u0445\\u043E\\u0434\\u0438\\u043C\\u0430 Java 11 \\u0438\\u043B\\u0438 \\u043D\\u043E\\u0432\\u0435\\u0435\nMessage.LoadReportFail               = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0437\\u0430\\u0433\\u0440\\u0443\\u0437\\u0438\\u0442\\u044C \\u043E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435 \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\nMessage.LoadingFile                  = \\u0417\\u0430\\u0433\\u0440\\u0443\\u0437\\u043A\\u0430 \\u0444\\u0430\\u0439\\u043B\\u0430...\nMessage.LocaleChange                 = \\u042F\\u0437\\u044B\\u043A \\u0438\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0430 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D \\u043D\\u0430:\nMessage.NewVersion                   = \\u041D\\u043E\\u0432\\u0430\\u044F \\u0432\\u0435\\u0440\\u0441\\u0438\\u044F jGnash \\u0434\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u0430 \\u0434\\u043B\\u044F \\u0437\\u0430\\u0433\\u0440\\u0443\\u0437\\u043A\\u0438.\nMessage.NoRepeat                     = \\u041D\\u0435 \\u043F\\u043E\\u0432\\u0442\\u043E\\u0440\\u044F\\u0435\\u0442\\u0441\\u044F\nMessage.OpenJfxDownload              = \\u0417\\u0430\\u0433\\u0440\\u0443\\u0436\\u0430\\u044E\\u0442\\u0441\\u044F \\u0431\\u0438\\u0431\\u043B\\u0438\\u043E\\u0442\\u0435\\u043A\\u0438 OpenJFX\\n\\n\\u041F\\u043E\\u0441\\u043B\\u0435 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0438\\u044F \\u043D\\u0435\\u043E\\u0431\\u0445\\u043E\\u0434\\u0438\\u043C\\u043E \\u043F\\u0435\\u0440\\u0435\\u0437\\u0430\\u043F\\u0443\\u0441\\u0442\\u0438\\u0442\\u044C \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u0443.\nMessage.OverwriteDB                  = \\u0421\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0443\\u044E\\u0449\\u0430\\u044F \\u0431\\u0430\\u0437\\u0430 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u0431\\u0443\\u0434\\u0435\\u0442 \\u043F\\u0435\\u0440\\u0435\\u0437\\u0430\\u043F\\u0438\\u0441\\u0430\\u043D\\u0430\nMessage.PackingFile                  = \\u0421\\u0436\\u0430\\u0442\\u0438\\u0435 \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445\nMessage.PackingFileComplete          = \\u0421\\u0436\\u0430\\u0442\\u0438\\u0435 \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043E\nMessage.ParseReportFail              = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u043E\\u0431\\u0440\\u0430\\u0431\\u043E\\u0442\\u0430\\u0442\\u044C \\u043E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435 \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\nMessage.PleaseWait                   = \\u041F\\u043E\\u0434\\u043E\\u0436\\u0434\\u0438\\u0442\\u0435\nMessage.PrefFail                     = \\u0421\\u0442\\u0430\\u0440\\u044B\\u0435 \\u043D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u043D\\u0435 \\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u044B\nMessage.PrepShutdown                 = \\u041F\\u043E\\u0434\\u0433\\u043E\\u0442\\u043E\\u0432\\u043A\\u0430 \\u043A \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0438\\u044E \\u0440\\u0430\\u0431\\u043E\\u0442\\u044B\nMessage.ProcessingReportData         = \\u041E\\u0431\\u0440\\u0430\\u0431\\u043E\\u0442\\u043A\\u0430 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\nMessage.Proxy                        = \\u0423\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u043A\\u0430 http \\u043F\\u0440\\u043E\\u043A\\u0441\\u0438:\nMessage.ReduceFont                   = \\u041F\\u043E\\u043F\\u0440\\u043E\\u0431\\u0443\\u0439\\u0442\\u0435 \\u0443\\u043C\\u0435\\u043D\\u044C\\u0448\\u0438\\u0442\\u044C \\u0440\\u0430\\u0437\\u043C\\u0435\\u0440 \\u0448\\u0440\\u0438\\u0444\\u0442\\u0430.\\n  \\u041F\\u043E\\u0434\\u0440\\u043E\\u0431\\u043D\\u0435\\u0435 \\u0441\\u043C\\u043E\\u0442\\u0440\\u0438\\u0442\\u0435 \\u0432 \\u0441\\u043F\\u0440\\u0430\\u0432\\u043A\\u0435 \\u043F\\u043E \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\\u043C.\nMessage.RemovingSecurityHistory      = \\u0423\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438 \\u0441\\u0442\\u043E\\u0438\\u043C\\u043E\\u0441\\u0442\\u0438 \\u0446\\u0435\\u043D\\u043D\\u043E\\u0439 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438 \\u0441 {0} \\u0438\\u0437 {1}\nMessage.ReportModLoaded              = \\u041C\\u043E\\u0434\\u0443\\u043B\\u0438 \\u0441\\u043E\\u0437\\u0434\\u0430\\u043D\\u0438\\u044F \\u043E\\u0442\\u0447\\u0435\\u0442\\u043E\\u0432 \\u0443\\u0441\\u043F\\u0435\\u0448\\u043D\\u043E \\u0437\\u0430\\u0433\\u0440\\u0443\\u0436\\u0435\\u043D\\u044B\nMessage.ReportWait                   = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430 \\u043F\\u043E\\u0434\\u043E\\u0436\\u0434\\u0438\\u0442\\u0435, \\u0437\\u0430\\u0433\\u0440\\u0443\\u0436\\u0430\\u044E\\u0442\\u0441\\u044F \\u043C\\u043E\\u0434\\u0443\\u043B\\u0438 \\u0441\\u043E\\u0437\\u0434\\u0430\\u043D\\u0438\\u044F \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\nMessage.RestartLocale                = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430, \\u043F\\u0435\\u0440\\u0435\\u0437\\u0430\\u043F\\u0443\\u0441\\u0442\\u0438\\u0442\\u0435 \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u0443 \\u0447\\u0442\\u043E\\u0431\\u044B \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0438\\u044F \\u0432\\u0441\\u0442\\u0443\\u043F\\u0438\\u043B\\u0438 \\u0432 \\u0441\\u0438\\u043B\\u0443\nMessage.SavingFile                   = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0435\\u043D\\u0438\\u0435 \\u0444\\u0430\\u0439\\u043B\\u0430...\nMessage.SearchWait                   = \\u041F\\u043E\\u0438\\u0441\\u043A, \\u043F\\u043E\\u0434\\u043E\\u0436\\u0434\\u0438\\u0442\\u0435\nMessage.Shutdown                     = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0438\\u0435 \\u0440\\u0430\\u0431\\u043E\\u0442\\u044B\nMessage.StartEndDate                 = \\u0412\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u0434\\u0430\\u0442\\u0443 \\u043D\\u0430\\u0447\\u0430\\u043B\\u0430 \\u0438 \\u043E\\u043A\\u043E\\u043D\\u0447\\u0430\\u043D\\u0438\\u044F\nMessage.StoreBackup                  = \\u0417\\u0430\\u043F\\u0438\\u0441\\u044C \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u043E\\u0439 \\u043A\\u043E\\u043F\\u0438\\u0438 \\u0432 \\u0444\\u0430\\u0439\\u043B:\nMessage.StoreComplete                = \\u0417\\u0430\\u043F\\u0438\\u0441\\u044C \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0430\nMessage.StoreWait                    = \\u041F\\u043E\\u0434\\u043E\\u0436\\u0434\\u0438\\u0442\\u0435, \\u0432\\u044B\\u043F\\u043E\\u043B\\u043D\\u044F\\u0435\\u0442\\u0441\\u044F \\u0441\\u043E\\u0445\\u0440\\u0430\\u043D\\u0435\\u043D\\u0438\\u0435 \\u0444\\u0430\\u0439\\u043B\\u0430...\nMessage.TXTFile                      = \\u0422\\u0435\\u043A\\u0441\\u0442\\u043E\\u0432\\u044B\\u0435 \\u0444\\u0430\\u0439\\u043B\\u044B (*.TXT)\nMessage.TransactionAccountLocked     = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0434\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044E: \\u043A\\u043E\\u0440\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0438\\u0440\\u0443\\u044E\\u0449\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442 (\\u0438\\u043B\\u0438 \\u0441\\u0447\\u0435\\u0442\\u0430) \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D(\\u044B)\nMessage.TransactionAdd               = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F \\u0434\\u043E\\u0431\\u0430\\u0432\\u043B\\u0435\\u043D\\u0430\nMessage.TransactionModifyLocked      = \\u042D\\u0442\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F \\u043D\\u0435 \\u043C\\u043E\\u0436\\u0435\\u0442 \\u0431\\u044B\\u0442\\u044C \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0430.\\n\\u0421\\u0447\\u0435\\u0442 \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u0440\\u043E\\u0432\\u0430\\u043D \\u0434\\u043B\\u044F \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0438\\u0439.\nMessage.TransactionRemove            = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F \\u0443\\u0434\\u0430\\u043B\\u0435\\u043D\\u0430\nMessage.TransactionRemoveLocked      = \\u041D\\u0435\\u0432\\u043E\\u0437\\u043C\\u043E\\u0436\\u043D\\u043E \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044E: \\u043A\\u043E\\u0440\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0438\\u0440\\u0443\\u044E\\u0449\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442 (\\u0438\\u043B\\u0438 \\u0441\\u0447\\u0435\\u0442\\u0430) \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D(\\u044B)\nMessage.UninstallBad                 = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0434\\u0435\\u0438\\u043D\\u0441\\u0442\\u0430\\u043B\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\nMessage.UninstallGood                = \\u0414\\u0435\\u0438\\u043D\\u0441\\u0442\\u0430\\u043B\\u044F\\u0446\\u0438\\u044F \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0430!\nMessage.UpdatedPrice                 = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0430 \\u0446\\u0435\\u043D\\u0430 \\u0434\\u043B\\u044F {0}\nMessage.UpdatedPriceDate             = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0430 \\u0446\\u0435\\u043D\\u0430 \\u0434\\u043B\\u044F {0}, {1}\nMessage.UpdatedSecurityEvent         = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u043E \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u0435 \\u0434\\u043B\\u044F {0}\nMessage.Version                      = \\u0412\\u044B \\u0438\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u0443\\u0435\\u0442\\u0435 \\u0432\\u0435\\u0440\\u0441\\u0438\\u044E\nMessage.Warn.CommodityInUse          = \\u0411\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440 \\u0438\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u0443\\u0435\\u0442\\u0441\\u044F\nMessage.Warn.ConfigAmortization      = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430, \\u043D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u0442\\u0435 \\u0430\\u043C\\u043E\\u0440\\u0442\\u0438\\u0437\\u0430\\u0446\\u0438\\u044E\nMessage.Warn.CurrencyInUse           = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u0438\\u0441\\u043F\\u043E\\u043B\\u044C\\u0437\\u0443\\u0435\\u0442\\u0441\\u044F\nMessage.Warn.FailedTransInfoRemoval  = \\u041D\\u0435 \\u0443\\u0434\\u0430\\u043B\\u043E\\u0441\\u044C \\u0443\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0438\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044E \\u043E \\u0441\\u0442\\u0430\\u0440\\u043E\\u0439 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nMessage.Warn.MoveFile                = \\u0424\\u0430\\u0439\\u043B \"{0}\" \\u0431\\u0443\\u0434\\u0435\\u0442 \\u043F\\u0435\\u0440\\u0435\\u043C\\u0435\\u0449\\u0435\\u043D \\n\\u0432 \"{1}\".\\n\\n\\u041F\\u0440\\u043E\\u0434\\u043E\\u043B\\u0436\\u0438\\u0442\\u044C?\nMessage.Warn.SameFile                = \\u0424\\u0430\\u0439\\u043B \"{0}\" \\u0443\\u0436\\u0435 \\u0441\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442 \\n \\u0432 \"{1}\".\\n\\n\\u0424\\u0430\\u0439\\u043B \\u043D\\u0435 \\u0431\\u0443\\u0434\\u0435\\u0442 \\u043F\\u0435\\u0440\\u0435\\u043C\\u0435\\u0449\\u0435\\u043D.\nMessage.Warn.WindowWidth             = \\u041E\\u043A\\u043D\\u043E \\u0441\\u043B\\u0438\\u0448\\u043A\\u043E\\u043C \\u043C\\u0430\\u043B\\u043E\\n\\n\\u0423\\u0432\\u0435\\u043B\\u0438\\u0447\\u044C\\u0442\\u0435 \\u0448\\u0438\\u0440\\u0438\\u043D\\u0443 \\u0438\\u043B\\u0438 \\u0443\\u043C\\u0435\\u043D\\u044C\\u0448\\u0438\\u0442\\u0435 \\u0440\\u0430\\u0437\\u043C\\u0435\\u0440 \\u0448\\u0440\\u0438\\u0444\\u0442\\u0430\n\nName.BankAccounts    = \\u0411\\u0430\\u043D\\u043A\\u043E\\u0432\\u0441\\u043A\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nName.ExpenseAccounts = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nName.IncomeAccounts  = \\u041F\\u0440\\u0438\\u0445\\u043E\\u0434\\u043D\\u044B\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\nName.Root            = \\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = \\u0421 {0,date,long} \\u043F\\u043E {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = \\u0421\\u0442\\u0440. {0} \\u0438\\u0437 {1}\nPattern.QuarterOfYear  = \\u041A\\u0432\\u0430\\u0440\\u0442\\u0430\\u043B {0,number,#} \\u0438\\u0437 {1,number,#}\nPattern.WeekOfYear     = \\u041D\\u0435\\u0434\\u0435\\u043B\\u044F {0,number,00} \\u0438\\u0437 {1,number,#}\n\nPeriod.10Min     = 10 \\u043C\\u0438\\u043D\\u0443\\u0442\nPeriod.15Min     = 15 \\u043C\\u0438\\u043D\\u0443\\u0442\nPeriod.1Day      = 1 \\u0434\\u0435\\u043D\\u044C\nPeriod.1Hr       = 1 \\u0447\\u0430\\u0441\nPeriod.2Hr       = 2 \\u0447\\u0430\\u0441\\u0430\nPeriod.30Min     = 30 \\u043C\\u0438\\u043D\\u0443\\u0442\nPeriod.5Min      = 5 \\u043C\\u0438\\u043D\\u0443\\u0442\nPeriod.8Hr       = 8 \\u0447\\u0430\\u0441\\u043E\\u0432\nPeriod.BiWeekly  = \\u0420\\u0430\\u0437 \\u0432 2 \\u043D\\u0435\\u0434\\u0435\\u043B\\u0438\nPeriod.Daily     = \\u0415\\u0436\\u0435\\u0434\\u043D\\u0435\\u0432\\u043D\\u043E\nPeriod.Monthly   = \\u0415\\u0436\\u0435\\u043C\\u0435\\u0441\\u044F\\u0447\\u043D\\u043E\nPeriod.NextStart = \\u041F\\u0440\\u0438 \\u0441\\u043B\\u0435\\u0434\\u0443\\u044E\\u0449\\u0435\\u043C \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A\\u0435\nPeriod.None      = \\u041D\\u0435\\u0442\nPeriod.OnlyOnce  = \\u0411\\u0435\\u0437 \\u043F\\u043E\\u0432\\u0442\\u043E\\u0440\\u0435\\u043D\\u0438\\u044F\nPeriod.Quarterly = \\u0415\\u0436\\u0435\\u043A\\u0432\\u0430\\u0440\\u0442\\u0430\\u043B\\u044C\\u043D\\u043E\nPeriod.Weekly    = \\u0415\\u0436\\u0435\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u043E\nPeriod.Yearly    = \\u0415\\u0436\\u0435\\u0433\\u043E\\u0434\\u043D\\u043E\n\nQuestion.DeleteAttachment = \\u042D\\u0442\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F \\u0441\\u043E\\u0434\\u0435\\u0440\\u0436\\u0438\\u0442 \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435.\\n\\n\\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0442\\u0430\\u043A\\u0436\\u0435 \\u0438 \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435?\n\nQuoteSource.None     = \\u041D\\u0435\\u0442\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = \\u0414\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434\nSecurityEvent.Price    = \\u0426\\u0435\\u043D\\u0430\nSecurityEvent.Split    = \\u0421\\u043F\\u043B\\u0438\\u0442\n\nSequence.EveryFifthRow  = \\u041A\\u0430\\u0436\\u0434\\u0430\\u044F \\u043F\\u044F\\u0442\\u0430\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430\nSequence.EveryForthRow  = \\u041A\\u0430\\u0436\\u0434\\u0430\\u044F \\u0447\\u0435\\u0442\\u0432\\u0435\\u0440\\u0442\\u0430\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430\nSequence.EveryOtherRow  = \\u0427\\u0435\\u0440\\u0435\\u0437 \\u043E\\u0434\\u043D\\u0443 \\u0441\\u0442\\u0440\\u043E\\u043A\\u0443\nSequence.EveryRow       = \\u041A\\u0430\\u0436\\u0434\\u0430\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430\nSequence.EverySecondRow = \\u041A\\u0430\\u0436\\u0434\\u0430\\u044F \\u0432\\u0442\\u043E\\u0440\\u0430\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430\nSequence.EveryThirdRow  = \\u041A\\u0430\\u0436\\u0434\\u0430\\u044F \\u0442\\u0440\\u0435\\u0442\\u044C\\u044F \\u0441\\u0442\\u0440\\u043E\\u043A\\u0430\n\nSortOrder.AccountBalanceDesc = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0441\\u0447\\u0435\\u0442\\u0430\nSortOrder.AccountName        = \\u041D\\u0430\\u0437\\u0432\\u0430\\u043D\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430\n\nState.Cleared       = \\u041E\nState.NotReconciled = \\u200B\nState.Reconciled    = \\u0421\n\nTab.About           = \\u041E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u0435\nTab.Accounts        = \\u0421\\u0447\\u0435\\u0442\\u0430\nTab.Adjust          = \\u041A\\u043E\\u0440\\u0440\\u0435\\u043A\\u0442\\u0438\\u0440\\u043E\\u0432\\u043A\\u0430\nTab.AppLicense      = \\u041B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u044F\nTab.Budgeting       = \\u0411\\u044E\\u0434\\u0436\\u0435\\u0442\nTab.Charge          = \\u041F\\u043E\\u043F\\u043E\\u043B\\u043D\\u0435\\u043D\\u0438\\u0435\nTab.Credit          = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\nTab.Credits         = \\u041E\\u0431 \\u0430\\u0432\\u0442\\u043E\\u0440\\u0430\\u0445\nTab.DataEngine      = \\u0411\\u0430\\u0437\\u0430 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445\nTab.DataProviders   = Data Providers\nTab.Day             = \\u0414\\u0435\\u043D\\u044C\nTab.Debit           = \\u0414\\u0435\\u0431\\u0435\\u0442\nTab.Formats         = \\u0424\\u043E\\u0440\\u043C\\u0430\\u0442\\u044B\nTab.GPLLicense      = \\u041B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u044F GPL\nTab.General         = \\u041E\\u0431\\u0449\\u0438\\u0435\nTab.LGPLLicense     = \\u041B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u044F LGPL\nTab.License         = \\u041B\\u0438\\u0446\\u0435\\u043D\\u0437\\u0438\\u044F\nTab.Month           = \\u041C\\u0435\\u0441\\u044F\\u0446\nTab.Network         = \\u0421\\u0435\\u0442\\u044C\nTab.None            = \\u041D\\u0435\\u0442\nTab.Payment         = \\u041F\\u043B\\u0430\\u0442\\u0435\\u0436\nTab.Register        = \\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTab.Reminders       = \\u041D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F\nTab.Report          = \\u041E\\u0442\\u0447\\u0435\\u0442\nTab.StartupShutdown = \\u0417\\u0430\\u043F\\u0443\\u0441\\u043A / \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0438\\u0435\nTab.SysInfo         = \\u0421\\u0438\\u0441\\u0442\\u0435\\u043C\\u043D\\u0430\\u044F \\u0438\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F\nTab.Transfer        = \\u041F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434\nTab.Week            = \\u041D\\u0435\\u0434\\u0435\\u043B\\u044F\nTab.Year            = \\u0413\\u043E\\u0434\n\nTag.Bank                   = \\u0411\\u0430\\u043D\\u043A\nTag.Dividend               = \\u0414\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434\nTag.FeesOffset             = \\u0417\\u0430\\u0447\\u0435\\u0442\\u044B \\u0441\\u0431\\u043E\\u0440\\u043E\\u0432\nTag.GainLoss               = \\u041F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C/(\\u0423\\u0431\\u044B\\u0442\\u043E\\u043A)\nTag.GainsOffset            = \\u0417\\u0430\\u0447\\u0435\\u0442\\u044B \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u0438\nTag.Investment             = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u043E\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0431\\u043E\\u0440\\u044B\nTag.InvestmentCashTransfer = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u043E\\u043D\\u043D\\u044B\\u0435 \\u0434\\u0435\\u043D\\u0435\\u0436\\u043D\\u044B\\u0435 \\u043F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434\\u044B\nTag.InvestmentFee          = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u043E\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0431\\u043E\\u0440\\u044B\nTag.LTCG                   = \\u0414\\u043E\\u043B\\u0433\\u043E\\u0441\\u0440\\u043E\\u0447\\u043D\\u043E\\u0435 \\u0440\\u0430\\u0441\\u043F\\u0440\\u0435\\u0434\\u0435\\u043B\\u0435\\u043D\\u0438\\u0435 \\u043F\\u0440\\u0438\\u0440\\u043E\\u0441\\u0442\\u0430 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0430\nTag.MTCG                   = \\u0421\\u0440\\u0435\\u0434\\u043D\\u0435\\u0441\\u0440\\u043E\\u0447\\u043D\\u043E\\u0435 \\u0440\\u0430\\u0441\\u043F\\u0440\\u0435\\u0434\\u0435\\u043B\\u0435\\u043D\\u0438\\u0435 \\u043F\\u0440\\u0438\\u0440\\u043E\\u0441\\u0442\\u0430 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0430\nTag.Misc                   = \\u0420\\u0430\\u0437\\u043D\\u043E\\u0435\nTag.NonTaxableInterest     = \\u041D\\u0435 \\u043E\\u0431\\u043B\\u0430\\u0433\\u0430\\u0435\\u043C\\u044B\\u0439 \\u043D\\u0430\\u043B\\u043E\\u0433\\u043E\\u043C \\u0434\\u043E\\u0445\\u043E\\u0434\nTag.STCG                   = \\u041A\\u0440\\u0430\\u0442\\u043A\\u043E\\u0441\\u0440\\u043E\\u0447\\u043D\\u043E\\u0435 \\u0440\\u0430\\u0441\\u043F\\u0440\\u0435\\u0434\\u0435\\u043B\\u0435\\u043D\\u0438\\u0435 \\u043F\\u0440\\u0438\\u0440\\u043E\\u0441\\u0442\\u0430 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0430\nTag.TaxableInterest        = \\u041D\\u0430\\u043B\\u043E\\u0433\\u043E\\u043E\\u0431\\u043B\\u0430\\u0433\\u0430\\u0435\\u043C\\u044B\\u0435 \\u0434\\u043E\\u0445\\u043E\\u0434\nTag.Vat                    = \\u041D\\u0414\\u0421\n\nTitle.About                      = \\u041E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043C\\u0435\nTitle.AccountBalance             = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0441\\u0447\\u0435\\u0442\\u0430\nTitle.AccountFilter              = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440\\u044B \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nTitle.AccountGroups              = \\u0413\\u0440\\u0443\\u043F\\u043F\\u044B \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nTitle.AccountInfo                = \\u0418\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F \\u043E \\u0441\\u0447\\u0435\\u0442\\u0435\nTitle.AccountRegister            = \\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442\\u0430\nTitle.AccountSecurities          = \\u0426\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438 \\u0441\\u0447\\u0435\\u0442\\u0430\nTitle.AddRemCurr                 = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C / \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u044B\nTitle.AmortizationSetup          = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0430 \\u0430\\u043C\\u043E\\u0440\\u0442\\u0438\\u0437\\u0430\\u0446\\u0438\\u0438\nTitle.Archive                    = \\u0410\\u0440\\u0445\\u0438\\u0432\nTitle.AutoComplete               = \\u0410\\u0432\\u0442\\u043E\\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0438\\u0435\nTitle.AutoSave                   = \\u0410\\u0432\\u0442\\u043E\\u0441\\u043E\\u0445\\u0440\\u0430\\u043D\\u0435\\u043D\\u0438\\u0435\nTitle.Available                  = \\u0414\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u043E\nTitle.BackgroundUpdate           = \\u041E\\u0431\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u0438\\u044F \\u0432 \\u0444\\u043E\\u043D\\u0435\nTitle.BackingStore               = \\u0420\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u0430\\u044F \\u043A\\u043E\\u043F\\u0438\\u044F\nTitle.BalanceSheet               = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u0432\\u044B\\u0439 \\u043E\\u0442\\u0447\\u0435\\u0442\nTitle.BaseColor                  = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0431\\u0430\\u0437\\u043E\\u0432\\u044B\\u0435 \\u0446\\u0432\\u0435\\u0442\\u0430\nTitle.BudgetGoal                 = \\u041C\\u0435\\u043D\\u0435\\u0434\\u0436\\u0435\\u0440 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0430\nTitle.BudgetManager              = \\u041C\\u0435\\u043D\\u0435\\u0434\\u0436\\u0435\\u0440 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0430\nTitle.BudgetProperties           = \\u0421\\u0432\\u043E\\u0439\\u0441\\u0442\\u0432\\u0430 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0430\nTitle.ButtonOrder                = \\u041F\\u043E\\u0440\\u044F\\u0434\\u043E\\u043A \\u043A\\u043D\\u043E\\u043F\\u043E\\u043A\nTitle.ChangePassword             = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nTitle.CheckDesign                = \\u0414\\u0438\\u0437\\u0430\\u0439\\u043D\\u0435\\u0440 \\u0447\\u0435\\u043A\\u043E\\u0432\nTitle.ChooseAccounts             = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430 \\u0434\\u043B\\u044F \\u0441\\u043E\\u0437\\u0434\\u0430\\u043D\\u0438\\u044F\nTitle.ColVis                     = \\u041E\\u0442\\u043E\\u0431\\u0440\\u0430\\u0436\\u0435\\u043D\\u0438\\u0435 \\u043A\\u043E\\u043B\\u043E\\u043D\\u043A\\u0438\nTitle.Colors                     = \\u0426\\u0432\\u0435\\u0442\\u0430\nTitle.CommoditiesSecurities      = \\u0411\\u0438\\u0440\\u0436\\u0435\\u0432\\u044B\\u0435 \\u0442\\u043E\\u0432\\u0430\\u0440\\u044B/\\u0446\\u0435\\u043D\\u043D\\u044B\\u0435 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438\nTitle.ConfigTransImportFilters   = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0438\\u0442\\u044C \\u0444\\u0438\\u043B\\u044C\\u0442\\u0440\\u044B \\u0438\\u043C\\u043F\\u043E\\u0440\\u0442\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTitle.Confirm                    = \\u041F\\u043E\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0438\\u0442\\u0435\nTitle.ConnectServer              = \\u041F\\u043E\\u0434\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u044C\\u0441\\u044F \\u043A \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440\\u0443\nTitle.Connection                 = \\u0421\\u043E\\u0435\\u0434\\u0438\\u043D\\u0435\\u043D\\u0438\\u0435\nTitle.Console                    = \\u041E\\u043A\\u043D\\u043E Java-\\u043A\\u043E\\u043D\\u0441\\u043E\\u043B\\u0438\nTitle.CreateModifyCommodities    = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C / \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0431\\u0438\\u0440\\u0436\\u0435\\u0432\\u044B\\u0435 \\u0442\\u043E\\u0432\\u0430\\u0440\\u044B\nTitle.Credits                    = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\\u044B\nTitle.Currencies                 = \\u0412\\u0430\\u043B\\u044E\\u0442\\u044B\nTitle.Current                    = \\u0422\\u0435\\u043A\\u0443\\u0449\\u0438\\u0439\nTitle.CutOffDate                 = \\u0414\\u0430\\u0442\\u0430 \\u0444\\u0438\\u043A\\u0441\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u043D\\u043E\\u0433\\u043E \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u0430:\nTitle.DatabaseCfg                = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0430 \\u0431\\u0430\\u0437\\u044B \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445\nTitle.DateFormats                = \\u0424\\u043E\\u0440\\u043C\\u0430\\u0442\\u044B \\u0434\\u0430\\u0442\\u044B\nTitle.Debits                     = \\u0414\\u0435\\u0431\\u0435\\u0442\\u043E\\u0432\\u044B\\u0435\nTitle.DefDefCurr                 = \\u041E\\u043F\\u0440\\u0435\\u0434\\u0435\\u043B\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nTitle.DefTranNum                 = \\u041D\\u043E\\u043C\\u0435\\u0440\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nTitle.DefaultBehavior            = \\u041F\\u043E\\u0432\\u0435\\u0434\\u0435\\u043D\\u0438\\u0435 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nTitle.Defaults                   = \\u0417\\u043D\\u0430\\u0447\\u0435\\u043D\\u0438\\u044F \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nTitle.DeleteAttachment           = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435\nTitle.Display                    = \\u0412\\u043D\\u0435\\u0448\\u043D\\u0438\\u0439 \\u0432\\u0438\\u0434\nTitle.DuplicateTransaction       = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043A\\u043E\\u043F\\u0438\\u044E \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nTitle.DuplicateTransactionsFound = \\u041D\\u0430\\u0439\\u0434\\u0435\\u043D\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F-\\u0434\\u0443\\u0431\\u043B\\u0438\\u043A\\u0430\\u0442\nTitle.EditExchangeRates          = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043A\\u0443\\u0440\\u0441\\u044B \\u043E\\u0431\\u043C\\u0435\\u043D\\u0430\nTitle.EndMonthBalance            = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u043F\\u043E \\u0441\\u0447\\u0435\\u0442\\u0443 \\u043D\\u0430 \\u043A\\u043E\\u043D\\u0435\\u0446 \\u043C\\u0435\\u0441\\u044F\\u0446\\u0430\nTitle.EnterPassword              = \\u0412\\u0432\\u0435\\u0434\\u0438\\u0442\\u0435 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nTitle.Entry                      = \\u0417\\u0430\\u043F\\u0438\\u0441\\u044C\nTitle.Error                      = \\u041E\\u0448\\u0438\\u0431\\u043A\\u0430\nTitle.EventHistory               = \\u0418\\u0441\\u0442\\u043E\\u0440\\u0438\\u044F \\u0441\\u043E\\u0431\\u044B\\u0442\\u0438\\u0439\nTitle.ExchangeRate               = \\u041A\\u0443\\u0440\\u0441 \\u043E\\u0431\\u043C\\u0435\\u043D\\u0430\nTitle.FileImport                 = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0444\\u0430\\u0439\\u043B \\u0434\\u043B\\u044F \\u0438\\u043C\\u043F\\u043E\\u0440\\u0442\\u0430\nTitle.FileLoginCredentials       = \\u0423\\u0447\\u0435\\u0442\\u043D\\u044B\\u0435 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0435\nTitle.Filters                    = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440\\u044B\nTitle.FontSize                   = \\u0420\\u0430\\u0437\\u043C\\u0435\\u0440 \\u0448\\u0440\\u0438\\u0444\\u0442\\u0430 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nTitle.Fonts                      = \\u0428\\u0440\\u0438\\u0444\\u0442\\u044B\nTitle.Frequency                  = \\u041F\\u0435\\u0440\\u0438\\u043E\\u0434\\u0438\\u0447\\u043D\\u043E\\u0441\\u0442\\u044C\nTitle.HTTPProxy                  = HTTP \\u043F\\u0440\\u043E\\u043A\\u0441\\u0438\nTitle.Help                       = \\u041F\\u043E\\u043C\\u043E\\u0449\\u044C \\u043F\\u043E jGnash\nTitle.HistoryImport              = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438\nTitle.ImageFiles                 = \\u0424\\u0430\\u0439\\u043B\\u044B \\u0438\\u0437\\u043E\\u0431\\u0440\\u0430\\u0436\\u0435\\u043D\\u0438\\u0439\nTitle.ImpPartQif                 = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0447\\u0430\\u0441\\u0442\\u044C QIF \\u0444\\u0430\\u0439\\u043B\\u0430\nTitle.ImpSum                     = \\u0418\\u0442\\u043E\\u0433\\u0438 \\u0438\\u043C\\u043F\\u043E\\u0440\\u0442\\u0430\nTitle.ImportOFX                  = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0438\\u0437 \\u0444\\u0430\\u0439\\u043B\\u0430 OFX\nTitle.ImportTransactions         = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439 \\u0438\\u0437 \\u0444\\u0430\\u0439\\u043B\\u0430\nTitle.IncomeExpenseBarChart      = \\u0414\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u044B/\\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B\"\nTitle.IncomeExpenseChart         = \\u041A\\u0440\\u0443\\u0433\\u043E\\u0432\\u0430\\u044F \\u0434\\u0438\\u0430\\u0433\\u0440\\u0430\\u043C\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u044B/\\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B\"\nTitle.Information                = \\u0418\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0438\\u044F\nTitle.InvFees                    = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u043E\\u043D\\u043D\\u044B\\u0435 \\u0441\\u0431\\u043E\\u0440\\u044B\nTitle.InvGainsLoss               = \\u0418\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0438\\u043E\\u043D\\u043D\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C / \\u0443\\u0431\\u044B\\u0442\\u043E\\u043A\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438\nTitle.ModOFXTrans                = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438 OFX\nTitle.ModQIFTrans                = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438 QIF\nTitle.ModifyAccount              = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\nTitle.ModifyCurrencies           = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u044B\nTitle.ModifyReminder             = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435\nTitle.ModifySecHistory           = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u044E \\u0446\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0431\\u0443\\u043C\\u0430\\u0433\nTitle.ModifyTransaction          = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044E\nTitle.MoveFile                   = \\u041F\\u0435\\u0440\\u0435\\u043C\\u0435\\u0441\\u0442\\u0438\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B\nTitle.NewAccount                 = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0441\\u0447\\u0435\\u0442\nTitle.NewBudget                  = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nTitle.NewFile                    = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0442\\u044C \\u043D\\u043E\\u0432\\u044B\\u0439 \\u0444\\u0430\\u0439\\u043B\nTitle.NewPassword                = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nTitle.NewReminder                = \\u041D\\u043E\\u0432\\u043E\\u0435 \\u043D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435\nTitle.NewTrans                   = \\u041D\\u043E\\u0432\\u0430\\u044F \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F\nTitle.Notes                      = \\u0417\\u0430\\u043C\\u0435\\u0442\\u043A\\u0438\nTitle.NumericFormats             = \\u0427\\u0438\\u0441\\u043B\\u043E\\u0432\\u044B\\u0435 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442\\u044B\nTitle.Open                       = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C\nTitle.Options                    = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = \\u0421\\u0436\\u0430\\u0442\\u044C \\u0431\\u0430\\u0437\\u0443 \\u0434\\u0430\\u043D\\u043D\\u044B\\u0445\nTitle.PageSetup                  = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u044B \\u0441\\u0442\\u0440\\u0430\\u043D\\u0438\\u0446\\u044B\nTitle.ParentAccount              = \\u0420\\u043E\\u0434\\u0438\\u0442\\u0435\\u043B\\u044C\\u0441\\u043A\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442\nTitle.PercentDist                = \\u041F\\u0440\\u043E\\u0446\\u0435\\u043D\\u0442\\u043D\\u043E\\u0435 \\u0440\\u0430\\u0441\\u043F\\u0440\\u0435\\u0434\\u0435\\u043B\\u0435\\u043D\\u0438\\u0435\nTitle.PercentExpense             = \\u0420\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B \\u0432 \\u043F\\u0440\\u043E\\u0446\\u0435\\u043D\\u0442\\u0430\\u0445\nTitle.PercentIncome              = \\u0414\\u043E\\u0445\\u043E\\u0434 \\u0432 \\u043F\\u0440\\u043E\\u0446\\u0435\\u043D\\u0442\\u0430\\u0445\nTitle.PleaseWait                 = \\u041F\\u043E\\u0436\\u0430\\u043B\\u0443\\u0439\\u0441\\u0442\\u0430 \\u043F\\u043E\\u0434\\u043E\\u0436\\u0434\\u0438\\u0442\\u0435\nTitle.PortfolioReport            = \\u041E\\u0442\\u0447\\u0435\\u0442 \\u043F\\u043E \\u043F\\u043E\\u0440\\u0442\\u0444\\u0435\\u043B\\u044E\nTitle.PriceHistory               = \\u0418\\u0441\\u0442\\u043E\\u0440\\u0438\\u044F \\u0446\\u0435\\u043D\nTitle.ProfitLoss                 = \\u0412\\u0435\\u0434\\u043E\\u043C\\u043E\\u0441\\u0442\\u044C \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u0435\\u0439 \\u0438 \\u0443\\u0431\\u044B\\u0442\\u043A\\u043E\\u0432\nTitle.ReconcileSettings          = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u0441\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u043D\\u0438\\u044F \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nTitle.Reminder                   = \\u041D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u0435\nTitle.Reminders                  = \\u041D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F\nTitle.RenameBudget               = \\u041F\\u0435\\u0440\\u0435\\u0438\\u043C\\u0435\\u043D\\u043E\\u0432\\u0430\\u0442\\u044C \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nTitle.ReportOptions              = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u043E\\u0442\\u0447\\u0435\\u0442\\u0430\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = \\u041D\\u0435\\u0440\\u0430\\u0441\\u043F\\u0440\\u0435\\u0434\\u0435\\u043B\\u0435\\u043D\\u043D\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C\nTitle.ReverseAccountBalances     = \\u0418\\u043D\\u0432\\u0435\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u043E\\u0442\\u043E\\u0431\\u0440\\u0430\\u0436\\u0435\\u043D\\u0438\\u0435 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u0432 \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u043A\\u0430\\u043A\nTitle.SaveFile                   = \\u0421\\u043E\\u0445\\u0440\\u0430\\u043D\\u0438\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B\nTitle.SecurityHistory            = \\u0418\\u0441\\u0442\\u043E\\u0440\\u0438\\u044F \\u0446\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0431\\u0443\\u043C\\u0430\\u0433\nTitle.SelAccount                 = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0441\\u0447\\u0435\\u0442\nTitle.SelAvailCurr               = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0434\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u044B\\u0435 \\u0432\\u0430\\u043B\\u044E\\u0442\\u044B\nTitle.SelDate                    = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0434\\u0430\\u0442\\u0443\nTitle.SelDefCurr                 = \\u0412\\u044B\\u0431\\u0440\\u0430\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u043F\\u043E \\u0443\\u043C\\u043E\\u043B\\u0447\\u0430\\u043D\\u0438\\u044E\nTitle.SelDefLocale               = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u044F\\u0437\\u044B\\u043A \\u0438\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0430\nTitle.SelDestAccount             = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0446\\u0435\\u043B\\u0435\\u0432\\u043E\\u0439 \\u0441\\u0447\\u0435\\u0442\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0441\\u0447\\u0435\\u0442 \\u0441\\u043E\\u0431\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0441\\u0440\\u0435\\u0434\\u0441\\u0442\\u0432\nTitle.SelFile                    = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0444\\u0430\\u0439\\u043B\nTitle.SelQifDateFormat           = \\u0423\\u043A\\u0430\\u0436\\u0438\\u0442\\u0435 \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442 \\u0434\\u0430\\u0442\\u044B QIF\nTitle.SelectColor                = \\u0412\\u044B\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0446\\u0432\\u0435\\u0442\nTitle.Selected                   = \\u0412\\u044B\\u0431\\u0440\\u0430\\u043D\\u043E\nTitle.Shutdown                   = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u0438\\u0435 \\u0440\\u0430\\u0431\\u043E\\u0442\\u044B\nTitle.SmartFill                  = \\u0423\\u043C\\u043D\\u043E\\u0435 \\u0437\\u0430\\u043F\\u043E\\u043B\\u043D\\u0435\\u043D\\u0438\\u0435\nTitle.SpitTran                   = \\u0421\\u043F\\u043B\\u0438\\u0442\nTitle.Startup                    = \\u0417\\u0430\\u043F\\u0443\\u0441\\u043A\nTitle.Steps                      = \\u042D\\u0442\\u0430\\u043F\\u044B\nTitle.Success                    = \\u0423\\u0441\\u043F\\u0435\\u0445\nTitle.Summary                    = \\u0418\\u0442\\u043E\\u0433\\u0438\nTitle.TagManager=Tag Manager\nTitle.Terms                      = \\u0422\\u0435\\u0440\\u043C\\u0438\\u043D\\u044B\nTitle.Transaction                = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044F\nTitle.TransactionImport          = \\u0418\\u043C\\u043F\\u043E\\u0440\\u0442 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTitle.TransactionList            = \\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTitle.TransactionSetup           = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u043A\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = \\u041D\\u0435\\u043E\\u0431\\u0440\\u0430\\u0431\\u043E\\u0442\\u0430\\u043D\\u043D\\u043E\\u0435 \\u0438\\u0441\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u0438\\u0435\nTitle.ViewImage                  = \\u041F\\u043E\\u0441\\u043C\\u043E\\u0442\\u0440\\u0435\\u0442\\u044C \\u0438\\u0437\\u043E\\u0431\\u0440\\u0430\\u0436\\u0435\\u043D\\u0438\\u0435\nTitle.Visible                    = \\u041E\\u0442\\u043E\\u0431\\u0440\\u0430\\u0436\\u0430\\u0435\\u0442\\u0441\\u044F\nTitle.VisibleAccountTypes        = \\u0412\\u0438\\u0434\\u0438\\u043C\\u044B\\u0435 \\u0442\\u0438\\u043F\\u044B \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nTitle.Warning                    = \\u041F\\u0440\\u0435\\u0434\\u0443\\u043F\\u0440\\u0435\\u0436\\u0434\\u0435\\u043D\\u0438\\u0435\n\nToolTip.AccountList                          = \\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432\nToolTip.AccountRegister                      = \\u0420\\u0435\\u0435\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439 \\u043F\\u043E \\u0441\\u0447\\u0435\\u0442\\u0443\nToolTip.AddAttachment                        = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435\nToolTip.BudgetMgr                            = \\u0421\\u043E\\u0437\\u0434\\u0430\\u043D\\u0438\\u0435, \\u0443\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0438 \\u0438\\u0437\\u043C\\u0435\\u043D\\u0435\\u043D\\u0438\\u0435 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u043E\\u0432\nToolTip.Budgeting                            = \\u0411\\u044E\\u0434\\u0436\\u0435\\u0442\nToolTip.ColumnVis                            = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C/\\u0441\\u043F\\u0440\\u044F\\u0442\\u0430\\u0442\\u044C \\u043A\\u043E\\u043B\\u043E\\u043D\\u043A\\u0443\nToolTip.ConvertSEntry                        = \\u041F\\u0440\\u0435\\u0432\\u0440\\u0430\\u0442\\u0438\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044E \\u0432 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u044E \\u0441 \\u0434\\u0432\\u043E\\u0439\\u043D\\u043E\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C\\u044E\nToolTip.DeleteAccount                        = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\nToolTip.DeleteAllExceptFridaySecurityHistory = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u0441\\u044E \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u044E \\u0437\\u0430 \\u0438\\u0441\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u0438\\u0435\\u043C \\u043F\\u044F\\u0442\\u043D\\u0438\\u0446\nToolTip.DeleteAllExceptMondaySecurityHistory = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u0441\\u044E \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u044E \\u0437\\u0430 \\u0438\\u0441\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u0438\\u0435\\u043C \\u043F\\u043E\\u043D\\u0435\\u0434\\u0435\\u043B\\u044C\\u043D\\u0438\\u043A\\u043E\\u0432\nToolTip.DeleteAttachment                     = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u0435\nToolTip.DeleteWeekendSecurityHistory         = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C \\u0441\\u0443\\u0431\\u0431\\u043E\\u0442\\u043D\\u0438\\u0435 \\u0438 \\u0432\\u043E\\u0441\\u043A\\u0440\\u0435\\u0441\\u043D\\u044B\\u0435 \\u0437\\u0430\\u043F\\u0438\\u0441\\u0438 \\u0432 \\u0438\\u0441\\u0442\\u043E\\u0440\\u0438\\u0438\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = \\u042D\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0432\\u044B\\u0431\\u0440\\u0430\\u043D\\u044B\\u0435 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438 \\u0432 \\u0444\\u0430\\u0439\\u043B CSV \\u0438\\u043B\\u0438 QFX\nToolTip.FilterAccount                        = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430\nToolTip.FilterAccounts                       = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\\u0430 \\u043F\\u043E \\u0442\\u0438\\u043F\\u0443\nToolTip.FilterMemo                           = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440 \\u043F\\u043E \\u0437\\u0430\\u043C\\u0435\\u0442\\u043A\\u0430\\u043C\nToolTip.FilterPayee                          = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440 \\u043F\\u043E \\u043A\\u043E\\u043D\\u0442\\u0440\\u0430\\u0433\\u0435\\u043D\\u0442\\u0443\nToolTip.FilterReconciledState                = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440 \\u043F\\u043E \\u0441\\u0442\\u0430\\u0442\\u0443\\u0441\\u0443\nToolTip.FilterTransactionAge                 = \\u0424\\u0438\\u043B\\u044C\\u0442\\u0440 \\u043F\\u043E \\u0441\\u0440\\u043E\\u043A\\u0443\nToolTip.FontSize                             = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0440\\u0430\\u0437\\u043C\\u0435\\u0440 \\u0448\\u0440\\u0438\\u0444\\u0442\\u0430\nToolTip.FuzzyMatch                           = \\u041F\\u043E\\u0438\\u0441\\u043A \\u043E\\u0441\\u043D\\u043E\\u0432\\u0430\\u043D \\u043F\\u043E \\u043F\\u043E\\u0441\\u043B\\u0435\\u0434\\u043D\\u0435\\u0439 \\u043F\\u043E\\u0445\\u043E\\u0436\\u0435\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441\\u0438\nToolTip.Help                                 = \\u041F\\u043E\\u043C\\u043E\\u0449\\u044C\nToolTip.ISIN                                 = \\u041C\\u0435\\u0436\\u0434\\u0443\\u043D\\u0430\\u0440\\u043E\\u0434\\u043D\\u044B\\u0439 \\u0438\\u0434\\u0435\\u043D\\u0442\\u0438\\u0444\\u0438\\u043A\\u0430\\u0446\\u0438\\u043E\\u043D\\u043D\\u044B\\u0439 \\u043A\\u043E\\u0434 \\u0446\\u0435\\u043D\\u043D\\u043E\\u0439 \\u0431\\u0443\\u043C\\u0430\\u0433\\u0438\nToolTip.IntegersOnly                         = \\u0422\\u043E\\u043B\\u044C\\u043A\\u043E \\u0446\\u0435\\u043B\\u044B\\u0435 \\u0447\\u0438\\u0441\\u043B\\u0430\nToolTip.ModifyAccount                        = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\nToolTip.NewAccount                           = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0441\\u0447\\u0435\\u0442\nToolTip.PageSetup                            = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0438 \\u0441\\u0442\\u0440\\u0430\\u043D\\u0438\\u0446\\u044B\nToolTip.PrintRegRep                          = \\u0420\\u0430\\u0441\\u043F\\u0435\\u0447\\u0430\\u0442\\u0430\\u0442\\u044C \\u0441\\u043F\\u0438\\u0441\\u043E\\u043A \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nToolTip.ReconcileAccount                     = \\u0421\\u043E\\u0433\\u043B\\u0430\\u0441\\u043E\\u0432\\u0430\\u0442\\u044C \\u0441\\u0447\\u0435\\u0442\nToolTip.Reminders                            = \\u041D\\u0430\\u043F\\u043E\\u043C\\u0438\\u043D\\u0430\\u043D\\u0438\\u044F\nToolTip.ResizeColumns                        = \\u0418\\u0437\\u043C\\u0435\\u043D\\u0438\\u0442\\u044C \\u0448\\u0438\\u0440\\u0438\\u043D\\u0443 \\u043A\\u043E\\u043B\\u043E\\u043D\\u043E\\u043A\nToolTip.ReversedCredit                       = \\u0418\\u043D\\u0432\\u0435\\u0440\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0437\\u043D\\u0430\\u043A \\u0441\\u0447\\u0435\\u0442\\u043E\\u0432 \\u043A\\u0440\\u0435\\u0434\\u0438\\u0442\\u043E\\u0432, \\u043E\\u0431\\u044F\\u0437\\u0430\\u0442\\u0435\\u043B\\u044C\\u0441\\u0442\\u0432, \\u0441\\u043E\\u0431\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0445 \\u0441\\u0440\\u0435\\u0434\\u0441\\u0442\\u0432 \\u0438 \\u0434\\u043E\\u0445\\u043E\\u0434\\u043E\\u0432\nToolTip.Scale                                = \\u041A\\u043E\\u043B\\u0438\\u0447\\u0435\\u0441\\u0442\\u0432\\u043E \\u0446\\u0438\\u0444\\u0440 \\u0441\\u043F\\u0440\\u0430\\u0432\\u0430 \\u043E\\u0442 \\u0437\\u0430\\u043F\\u044F\\u0442\\u043E\\u0439\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u044C \\u0434\\u0435\\u0442\\u0430\\u043B\\u0438\nToolTip.Timestamp                            = \\u0421\\u043E\\u0437\\u0434\\u0430\\u0432\\u0430\\u0442\\u044C \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u0443\\u044E \\u043A\\u043E\\u043F\\u0438\\u044E, \\u0438\\u043C\\u044F \\u0444\\u0430\\u0439\\u043B\\u0430 \\u0431\\u0443\\u0434\\u0435\\u0442 \\u0432\\u043A\\u043B\\u044E\\u0447\\u0430\\u0442\\u044C \\u0432 \\u0441\\u0435\\u0431\\u044F \\u0434\\u0430\\u0442\\u0443 \\u0438 \\u0432\\u0440\\u0435\\u043C\\u044F\nToolTip.ViewAttachment                       = \\u041F\\u0440\\u043E\\u0441\\u043C\\u043E\\u0442\\u0440 \\u0432\\u043B\\u043E\\u0436\\u0435\\u043D\\u0438\\u044F\nToolTip.ZoomRegister                         = \\u041E\\u0442\\u043A\\u0440\\u044B\\u0442\\u044C \\u0440\\u0435\\u0435\\u0441\\u0442\\u0440 \\u0432 \\u043D\\u043E\\u0432\\u043E\\u043C \\u043E\\u043A\\u043D\\u0435\n\nTransaction.AddShare        = \\u0412\\u043D\\u0435\\u0441\\u0435\\u043D\\u0438\\u0435 \\u0426\\u0411\nTransaction.BuyShare        = \\u041F\\u043E\\u043A\\u0443\\u043F\\u043A\\u0430 \\u0426\\u0411\nTransaction.Dividend        = \\u0414\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434\\u044B\nTransaction.DoubleEntry     = \\u0414\\u0432\\u043E\\u0439\\u043D\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTransaction.MergeShare      = \\u041E\\u0431\\u044A\\u0435\\u0434\\u0438\\u043D\\u0435\\u043D\\u0438\\u0435 \\u0426\\u0411\nTransaction.ReinvestDiv     = \\u0420\\u0435\\u0438\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u043D\\u0438\\u0435 \\u0434\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434\\u043E\\u0432\nTransaction.RemoveShare     = \\u0418\\u0437\\u044A\\u044F\\u0442\\u0438\\u0435 \\u0426\\u0411\nTransaction.ReturnOfCapital = \\u0412\\u043E\\u0437\\u0432\\u0440\\u0430\\u0442 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0430\nTransaction.SellShare       = \\u041F\\u0440\\u043E\\u0434\\u0430\\u0436\\u0430 \\u0426\\u0411\nTransaction.SingleEntry     = \\u041F\\u0440\\u043E\\u0441\\u0442\\u0430\\u044F \\u0437\\u0430\\u043F\\u0438\\u0441\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439\nTransaction.Split           = \\u0421\\u043F\\u043B\\u0438\\u0442\nTransaction.SplitEntry      = \\u0427\\u0430\\u0441\\u0442\\u044C \\u0441\\u043F\\u043B\\u0438\\u0442\\u0430\nTransaction.SplitShare      = \\u0420\\u0430\\u0437\\u0434\\u0435\\u043B\\u0435\\u043D\\u0438\\u0435 \\u0426\\u0411\nTransaction.TransferIn      = \\u0412\\u0445\\u043E\\u0434\\u044F\\u0449\\u0438\\u0439 \\u043F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434\nTransaction.TransferOut     = \\u0418\\u0441\\u0445\\u043E\\u0434\\u044F\\u0449\\u0438\\u0439 \\u043F\\u0435\\u0440\\u0435\\u0432\\u043E\\u0434\n\nWord.Add             = \\u0414\\u043E\\u0431\\u0430\\u0432\\u0438\\u0442\\u044C\nWord.All             = \\u0412\\u0441\\u0435\nWord.Balance         = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\nWord.Buy             = \\u041F\\u043E\\u043A\\u0443\\u043F\\u043A\\u0430\nWord.Commodity       = \\u0411\\u0438\\u0440\\u0436\\u0435\\u0432\\u043E\\u0439 \\u0442\\u043E\\u0432\\u0430\\u0440\nWord.Copy            = \\u041A\\u043E\\u043F\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C\nWord.Description     = \\u041E\\u043F\\u0438\\u0441\\u0430\\u043D\\u0438\\u0435\nWord.Difference      = \\u0420\\u0430\\u0437\\u043D\\u0438\\u0446\\u0430\nWord.Dividend        = \\u0414\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434\\u044B\nWord.Exchange        = \\u041E\\u0431\\u043C\\u0435\\u043D\nWord.Fees            = \\u0421\\u0431\\u043E\\u0440\\u044B\nWord.GrossExpense    = \\u0418\\u0422\\u041E\\u0413\\u041E \\u0440\\u0430\\u0441\\u0445\\u043E\\u0434\\u044B\nWord.GrossIncome     = \\u0418\\u0422\\u041E\\u0413\\u041E \\u0434\\u043E\\u0445\\u043E\\u0434\\u044B\nWord.Interest        = \\u0412\\u044B\\u0433\\u043E\\u0434\\u0430\nWord.Into            = \\u0432\nWord.Invalid         = \\u041D\\u0435\\u0432\\u0435\\u0440\\u043D\\u043E\nWord.Merge           = \\u041E\\u0431\\u044A\\u0435\\u0434\\u0438\\u043D\\u0438\\u0442\\u044C\nWord.Monthly         = \\u0415\\u0436\\u0435\\u043C\\u0435\\u0441\\u044F\\u0447\\u043D\\u043E\nWord.Name            = \\u041D\\u0430\\u0437\\u0432\\u0430\\u043D\\u0438\\u0435\nWord.NetIncome       = \\u0427\\u0438\\u0441\\u0442\\u0430\\u044F \\u043F\\u0440\\u0438\\u0431\\u044B\\u043B\\u044C\nWord.NetWorth        = \\u0421\\u043E\\u0431\\u0441\\u0442\\u0432\\u0435\\u043D\\u043D\\u044B\\u0439 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\nWord.NewBudget       = \\u041D\\u043E\\u0432\\u044B\\u0439 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nWord.None            = \\u041F\\u0443\\u0441\\u0442\\u043E\nWord.Quarterly       = \\u0415\\u0436\\u0435\\u043A\\u0432\\u0430\\u0440\\u0442\\u0430\\u043B\\u044C\\u043D\\u043E\nWord.ReInvDiv        = \\u0420\\u0435\\u0438\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0440\\u043E\\u0432\\u0430\\u0442\\u044C \\u0434\\u0438\\u0432\\u0438\\u0434\\u0435\\u043D\\u0434\\u044B\nWord.Remove          = \\u0423\\u0434\\u0430\\u043B\\u0438\\u0442\\u044C\nWord.ReturnOfCapital = \\u0412\\u043E\\u0437\\u0432\\u0440\\u0430\\u0442 \\u043A\\u0430\\u043F\\u0438\\u0442\\u0430\\u043B\\u0430\nWord.Seconds         = \\u0441\\u0435\\u043A\\u0443\\u043D\\u0434\nWord.Security        = \\u0426\\u0435\\u043D\\u043D\\u0430\\u044F \\u0431\\u0443\\u043C\\u0430\\u0433\\u0430\nWord.Sell            = \\u041F\\u0440\\u043E\\u0434\\u0430\\u0436\\u0430\nWord.Split           = \\u0420\\u0430\\u0437\\u0434\\u0435\\u043B\\u0438\\u0442\\u044C\nWord.Subtotal        = \\u041F\\u043E\\u0434\\u0438\\u0442\\u043E\\u0433\nWord.Total           = \\u0418\\u0442\\u043E\\u0433\\u043E\nWord.Totals          = \\u0418\\u0442\\u043E\\u0433\nWord.Yearly          = \\u0415\\u0436\\u0435\\u0433\\u043E\\u0434\\u043D\\u043E\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_uk.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = \\u0410\\u043A\\u0442\\u0438\\u0432\\u0438, \\u043C\\u0430\\u0439\\u043D\\u043E\nAccountType.Bank             = \\u0411\\u0430\\u043D\\u043A\\u0456\\u0432\\u0441\\u044C\\u043A\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nAccountType.Cash             = \\u0413\\u043E\\u0442\\u0456\\u0432\\u043A\\u0430\nAccountType.Checking         = \\u0427\\u0435\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nAccountType.Credit           = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\\u0438\nAccountType.Equity           = \\u041F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u0438\\u0439 \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B\nAccountType.Expense          = \\u0412\\u0438\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nAccountType.Income           = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nAccountType.Investment       = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0457\nAccountType.Liability        = \\u0417\\u043E\\u0431\\u043E\\u0432'\\u044F\\u0437\\u0430\\u043D\\u043D\\u044F\nAccountType.MoneyMarket      = \\u0413\\u0440\\u043E\\u0448\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0438\\u043D\\u043E\\u043A\nAccountType.Mutual           = \\u0421\\u043F\\u0456\\u043B\\u044C\\u043D\\u0456 \\u0444\\u043E\\u043D\\u0434\\u0438\nAccountType.Root             = \\u041F\\u0435\\u0440\\u0435\\u043B\\u0456\\u043A \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\nAccountType.SimpleInvestment = \\u043F\\u0440\\u043E\\u0441\\u0442\\u0438\\u0439 \\u0456\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0439\\u043D\\u0438\\u0439\n\nButton.AccTerms                = \\u0412\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0431\\u0443\\u0445\\u0433\\u0430\\u043B\\u0442\\u0435\\u0440\\u0441\\u044C\\u043A\\u0438 \\u0442\\u0435\\u0440\\u043C\\u0456\\u043D\\u0438\nButton.Accounts                = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.AckSel                  = \\u041F\\u0456\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0438\\u0442\\u0438 \\u0432\\u0438\\u0431\\u0440\\u0430\\u043D\\u0435\nButton.Add                     = \\u0414\\u043E\\u0434\\u0430\\u0442\\u0438\nButton.AllDates                = \\u0432\\u0441\\u0435 \\u0414\\u0430\\u0442\\u0438\nButton.Amortize                = \\u0410\\u043C\\u043E\\u0440\\u0442\\u0438\\u0437\\u0430\\u0446\\u0456\\u044F\nButton.AnimationsEnabled       = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u043D\\u044F \\u0435\\u0444\\u0435\\u043A\\u0442\\u0456\\u0432 \\u0430\\u043D\\u0456\\u043C\\u0430\\u0446\\u0456\\u0457\nButton.AnyStatus               = \\u0431\\u0443\\u0434\\u044C-\\u0441\\u0442\\u0430\\u0442\\u0443\\u0441\nButton.Apply                   = \\u041F\\u0440\\u0438\\u0439\\u043D\\u044F\\u0442\\u0438\nButton.AssetAccounts           = \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438 \\u0430\\u043A\\u0442\\u0438\\u0432\\u0456\\u0432\nButton.AutoReconcile           = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u043E \\u043F\\u043E\\u0433\\u043E\\u0434\\u0438\\u0442\\u0438 \\u0441\\u043A\\u043B\\u0430\\u0434\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nButton.AutoResizeColumns       = Automatically Resize Table Columns\nButton.AvailSecurities         = \\u0414\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u0456 \\u0446\\u0456\\u043D\\u043D\\u0456 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0438\nButton.Back                    = \\u041D\\u0430\\u0437\\u0430\\u0434\nButton.BankAccounts            = \\u0411\\u0430\\u043D\\u043A\\u0456\\u0432\\u0441\\u044C\\u043A\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.BudgetMgr               = \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442 \\u043C\\u0435\\u043D\\u0435\\u0434\\u0436\\u0435\\u0440\nButton.Budgeting               = \\u0441\\u043A\\u043B\\u0430\\u0434\\u0430\\u043D\\u043D\\u044F \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0443\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = \\u0421\\u043A\\u0430\\u0441\\u0443\\u0432\\u0430\\u0442\\u0438\nButton.CheckForUpdates         = \\u041F\\u0435\\u0440\\u0435\\u0432\\u0456\\u0440\\u0442\\u0435 \\u043D\\u0430\\u044F\\u0432\\u043D\\u0456\\u0441\\u0442\\u044C \\u043D\\u043E\\u0432\\u0438\\u0445 \\u043E\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u044C\nButton.CheckReminders          = \\u041F\\u0435\\u0440\\u0435\\u0432\\u0456\\u0440\\u0438\\u0442\\u0438 \\u043F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nButton.Clear                   = \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u0438\nButton.ClearAll                = \\u041E\\u0447\\u0438\\u0441\\u0442\\u0438\\u0442\\u0438 \\u0432\\u0441\\u0435\nButton.Cleared                 = \\u041E\\u0447\\u0438\\u0449\\u0435\\u043D\\u043E\nButton.Close                   = \\u0417\\u0430\\u043A\\u0440\\u0438\\u0442\\u0438\nButton.Compare                 = \\u043F\\u043E\\u0440\\u0456\\u0432\\u043D\\u044F\\u0442\\u0438\nButton.ConcatenateMemos        = \\u043A\\u043E\\u043C\\u0431\\u0456\\u043D\\u0430\\u0442 Memos\nButton.ConfirmReminderDelete   = \\u0417\\u0430\\u043F\\u0438\\u0442 \\u043F\\u0440\\u0438 \\u0432\\u0438\\u0434\\u0430\\u043B\\u0435\\u043D\\u043D\\u0456 \\u043D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nButton.ConfirmTransDelete      = \\u0417\\u0430\\u043F\\u0438\\u0442 \\u043F\\u0440\\u0438 \\u0432\\u0438\\u0434\\u0430\\u043B\\u0435\\u043D\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nButton.CopyToClip              = \\u041A\\u043E\\u043F\\u0456\\u044E\\u0432\\u0430\\u0442\\u0438 \\u0434\\u043E \\u0431\\u0443\\u0444\\u0435\\u0440\\u0443\nButton.CreateTimeFile          = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B \\u0437 \\u0434\\u0430\\u0442\\u043E\\u044E \\u0442\\u0430 \\u0447\\u0430\\u0441\\u043E\\u043C \\u0432 \\u0456\\u043C\\u0435\\u043D\\u0456 \\u043F\\u0456\\u0434 \\u0447\\u0430\\u0441 \\u0432\\u0438\\u0445\\u043E\\u0434\\u0443\nButton.CreditAccounts          = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\\u043D\\u0456 \\u0420\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.Delete                  = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438\nButton.DeleteAll               = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0432\\u0441\\u0435\nButton.DeleteWeekends          = \\u0432\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0412\\u0438\\u0445\\u0456\\u0434\\u043D\\u0456 \\u0434\\u043D\\u0456\nButton.DetailSplits            = \\u0421\\u043A\\u043B\\u0430\\u0434\\u0435\\u043D\\u0430 \\u0434\\u043E\\u043A\\u043B\\u0430\\u0434\\u043D\\u043E\nButton.Duplicate               = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043A\\u043E\\u043F\\u0456\\u044E\nButton.Edit                    = \\u0420\\u0435\\u0434\\u0430\\u0433\\u0443\\u0432\\u0430\\u0442\\u0438\nButton.EnableAutoComplete      = \\u0412\\u0432\\u0456\\u043C\\u043A\\u043D\\u0443\\u0442\\u0438 \\u0430\\u0432\\u0442\\u043E-\\u0434\\u043E\\u043F\\u043E\\u0432\\u043D\\u0435\\u043D\\u043D\\u044F \\u043F\\u0440\\u0438 \\u0432\\u0432\\u043E\\u0434\\u0456 \\u0442\\u0435\\u043A\\u0441\\u0442\\u0443\nButton.Enabled                 = \\u0412\\u0432\\u0456\\u043C\\u043A\\u043D\\u0435\\u043D\\u043E\nButton.EndingBalance           = \\u0437\\u0430\\u043B\\u0438\\u0448\\u043E\\u043A \\u043D\\u0430 \\u043A\\u0456\\u043D\\u0435\\u0446\\u044C \\u043F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0443\nButton.Enter                   = \\u0412\\u0432\\u0435\\u0441\\u0442\\u0438\nButton.EnterDaysBefore         = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u043E \\u0432\\u043D\\u043E\\u0441\\u0438\\u0442\\u0438 \\u0437\\u0430 .. \\u0434\\u043D\\u0456\\u0432\nButton.ExcludeFromBudget       = \\u0412\\u0438\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u0438 \\u0437 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\\u0456\\u0432\nButton.ExecuteNow              = Execute Now\nButton.ExpenseAccounts         = \\u0412\\u0438\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.Export                  = \\u0435\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\nButton.ExportSpreadsheet       = \\u0435\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442 \\u0442\\u0430\\u0431\\u043B\\u0438\\u0446\\u0456\nButton.Filter                  = \\u0424\\u0456\\u043B\\u044C\\u0442\\u0440\nButton.Finish                  = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0438\nButton.FinishLater             = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0438 \\u043F\\u0456\\u0437\\u043D\\u0456\\u0448\\u0435\nButton.ForceDefaultCurrency    = \\u0417\\u043C\\u0443\\u0448\\u0443\\u0454 \\u0432\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0438 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\\u043C\nButton.ForceGC                 = \\u0424\\u043E\\u0440\\u0441\\u0443\\u0432\\u0430\\u0442\\u0438 garbage collection\nButton.HTTPAuth                = \\u041D\\u0435\\u043E\\u0431\\u0445\\u0456\\u0434\\u043D\\u0430 \\u0430\\u0443\\u0442\\u0435\\u043D\\u0442\\u0438\\u0444\\u0456\\u043A\\u0430\\u0446\\u0456\\u044F\nButton.Hidden                  = \\u041F\\u0440\\u0438\\u0445\\u043E\\u0432\\u0430\\u043D\\u0456\nButton.HideAccount             = \\u041F\\u0440\\u0438\\u0445\\u043E\\u0432\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nButton.HideLockedAccount       = \\u0421\\u0445\\u043E\\u0432\\u0430\\u0442\\u0438 \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.HidePlaceholderAccount  = \\u0421\\u0445\\u043E\\u0432\\u0430\\u0442\\u0438 \\u0432\\u0456\\u0440\\u0442\\u0443\\u0430\\u043B\\u044C\\u043D\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.HideZeroBalance         = \\u0421\\u0445\\u043E\\u0432\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438 \\u0437 \\u043D\\u0443\\u043B\\u044C\\u044E\\u0432\\u0438\\u043C \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u043C\nButton.HistoricalFill          = \\u0456\\u0441\\u0442\\u043E\\u0440\\u0438\\u0447\\u043D\\u0456 \\u0437\\u0430\\u043F\\u043E\\u0432\\u043D\\u044E\\u0454\nButton.Horizontal              = \\u0413\\u043E\\u0440\\u0438\\u0437\\u043E\\u043D\\u0442\\u0430\\u043B\\u044C\\u043D\\u0438\\u0439\nButton.IncludeSubAccounts      = \\u0412\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u0438 \\u0441\\u0443\\u0431\\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.IncomeAccounts          = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nButton.IncomeAndExpense        = \\u0414\\u043E\\u0445\\u043E\\u0434\\u0438 \\u0456 \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438\nButton.Insert                  = \\u0412\\u0441\\u0442\\u0430\\u0432\\u0438\\u0442\\u0438\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = \\u0406\\u043D\\u0432\\u0435\\u0442\\u0440\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0432\\u0438\\u0434\\u0456\\u043B\\u0435\\u043D\\u043D\\u044F\nButton.Jump                    = \\u041F\\u0435\\u0440\\u0435\\u0439\\u0442\\u0438\nButton.KeepFridays             = Keep \\u043F'\\u044F\\u0442\\u043D\\u0438\\u0446\\u044C\nButton.KeepMondays             = Keep \\u043F\\u043E\\u043D\\u0435\\u0434\\u0456\\u043B\\u043A\\u0438\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u041E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456 120 \\u0434\\u043D\\u0456\\u0432\nButton.Last12Months            = \\u0417\\u0430 \\u043E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456 12 \\u043C\\u0456\\u0441\\u044F\\u0446\\u0456\\u0432\nButton.Last30Days              = \\u041E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456 30 \\u0434\\u043D\\u0456\\u0432\nButton.Last60Days              = \\u041E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456 60 \\u0434\\u043D\\u0456\\u0432\nButton.Last90Days              = \\u0417\\u0430 \\u043E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456 90 \\u0434\\u043D\\u0456\\u0432\nButton.LiabilityAccounts       = \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438 \\u043A\\u0440\\u0435\\u0434\\u0438\\u0442\\u043E\\u0440\\u0441\\u044C\\u043A\\u043E\\u0457 \\u0437\\u0430\\u0431\\u043E\\u0440\\u0433\\u043E\\u0432\\u0430\\u043D\\u043E\\u0441\\u0442\\u0456\nButton.Locked                  = \\u0417\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439\nButton.MatchAccountOnly        = Match \\u0432\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u044E\\u0447\\u0438 \\u0442\\u0456\\u043B\\u044C\\u043A\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u043A\\u043E\\u043D\\u043A\\u0440\\u0435\\u0442\\u043D\\u0438\\u0445 \\u0443\\u0433\\u043E\\u0434\nButton.MatchAllTrans           = \\u041C\\u0430\\u0442\\u0447 \\u0437 \\u0432\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0430\\u043D\\u043D\\u044F\\u043C \\u0432\\u0441\\u0456\\u0445 \\u0443\\u0433\\u043E\\u0434\nButton.MatchCaseSensitive      = \\u041C\\u0430\\u0442\\u0447 \\u0447\\u0443\\u0442\\u043B\\u0438\\u0432\\u0438\\u0439 \\u0434\\u043E \\u0440\\u0435\\u0433\\u0456\\u0441\\u0442\\u0440\\u0443\nButton.Modify                  = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438\nButton.MonthlyBalance          = \\u0449\\u043E\\u043C\\u0456\\u0441\\u044F\\u0447\\u043D\\u0438\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\nButton.New                     = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438\nButton.NewEmpty                = New Empty\nButton.NewHist                 = New Historical\nButton.NewPayment              = \\u041D\\u043E\\u0432\\u0438\\u0439 \\u043F\\u043B\\u0430\\u0442\\u0456\\u0436\nButton.Next                    = \\u041D\\u0430\\u0441\\u0442\\u0443\\u043F\\u043D\\u0438\\u0439\nButton.No                      = \\u041D\\u0456\nButton.NoEndDate               = \\u041D\\u0435\\u043C\\u0430 \\u043A\\u0456\\u043D\\u0446\\u0435\\u0432\\u043E\\u0457 \\u0434\\u0430\\u0442\\u0438\nButton.None                    = \\u0436\\u043E\\u0434\\u0435\\u043D\nButton.NotReconciled           = \\u0427\\u0438 \\u043D\\u0435 \\u0437\\u0432\\u0456\\u0440\\u0435\\u043D\\u0430\nButton.Ok                      = OK\nButton.Open                    = \\u0412\\u0456\\u0434\\u043A\\u0440\\u0438\\u0442\\u0438\nButton.OpenLastOnStartup       = \\u0412\\u0456\\u0434\\u043A\\u0440\\u0438\\u0442\\u0438 \\u043E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456\\u0439 \\u0444\\u0430\\u0439\\u043B \\u043F\\u0440\\u0438 \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A\\u0443\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u0438 \\u0441\\u0442\\u043E\\u0440\\u0456\\u043D\\u043A\\u0438\nButton.PlaceHolder             = \\u0412\\u0456\\u0440\\u0442\\u0443\\u0430\\u043B\\u044C\\u043D\\u0438\\u0439\nButton.Portrait                = Portrait\nButton.Print                   = \\u0414\\u0440\\u0443\\u043A\\u0443\\u0432\\u0430\\u0442\\u0438\nButton.PrintSample             = \\u0414\\u0440\\u0443\\u043A\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0437\\u0440\\u0430\\u0437\\u043E\\u043A\nButton.Properties              = \\u0432\\u043B\\u0430\\u0441\\u0442\\u0438\\u0432\\u043E\\u0441\\u0442\\u0456\nButton.Reconcile               = \\u0423\\u0437\\u0433\\u043E\\u0434\\u0438\\u0442\\u0438\nButton.ReconcileBoth           = \\u0423\\u0441\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457 \\u043C\\u0430\\u044E\\u0442\\u044C \\u043E\\u0434\\u043D\\u0430\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0441\\u0442\\u0430\\u043D \\u0443\\u0437\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u043D\\u044F\nButton.ReconcileDisable        = \\u0412\\u0456\\u0434\\u043A\\u043B\\u044E\\u0447\\u0438\\u0442\\u0438 \\u0430\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u0435 \\u0443\\u0437\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u043D\\u044F\nButton.ReconcileIncomeExpense  = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u0435 \\u0443\\u0437\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u043D\\u044F \\u0434\\u043B\\u044F \\u043F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u043E\\u0432\\u0438\\u0445 \\u0442\\u0430 \\u0432\\u0438\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0438\\u0445 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\nButton.Reconciled              = \\u0423\\u0437\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u0438\\u0439\nButton.Refresh                 = \\u041E\\u0431\\u043D\\u043E\\u0432\\u0438\\u0442\\u0438\nButton.RegDate                 = \\u041F\\u0430\\u043C'\\u044F\\u0442\\u0430\\u0442\\u0438 \\u0434\\u0430\\u0442\\u0443 \\u043E\\u0441\\u0442\\u0430\\u043D\\u043D\\u044C\\u043E\\u0457 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nButton.Register                = \\u0420\\u0435\\u0454\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nButton.RegisterFollowsList     = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u043E \\u0432\\u0438\\u0431\\u0438\\u0440\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0432 \\u0440\\u0435\\u0454\\u0441\\u0442\\u0440\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nButton.RememberPassword        = \\u041F\\u0430\\u043C'\\u044F\\u0442\\u0430\\u0442\\u0438 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nButton.RemindLater             = \\u041D\\u0430\\u0433\\u0430\\u0434\\u0430\\u0442\\u0438 \\u043F\\u0456\\u0437\\u043D\\u0456\\u0448\\u0435\nButton.Reminders               = \\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nButton.RemoteServer            = \\u0412\\u0456\\u0434\\u0434\\u0430\\u043B\\u0435\\u043D\\u0438\\u0439 \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440\nButton.Remove                  = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438\nButton.RemoveOldBackups        = Remove old backups\nButton.Rename                  = Rename\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = Reset All\nButton.Resize                  = \\u0410\\u0432\\u0442\\u043E-\\u0440\\u043E\\u0437\\u043C\\u0456\\u0440\nButton.RestoreDefault          = Restore default\nButton.RestoreLastTranTab      = Restore last used transaction tab\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\nButton.Save                    = \\\\u0417\\\\u0431\\\\u0435\\\\u0440\\\\u0456\\\\u0433\\\\u0442\\\\u0438\nButton.SaveFilters             = Save Filters\nButton.SaveImage               = Save Image\nButton.Select                  = \\u0412\\u0438\\u0434\\u0456\\u043B\\u0438\\u0442\\u0438\nButton.SelectAll               = \\u0412\\u0438\\u0434\\u0456\\u043B\\u0438\\u0442\\u0438 \\u0432\\u0441\\u0456\nButton.SelectText              = \\u0412\\u0438\\u0434\\u0456\\u043B\\u0438\\u0442\\u0438 \\u0442\\u0435\\u043A\\u0441\\u0442 \\u0432 \\u0444\\u043E\\u043A\\u0443\\u0441\\u0456\nButton.ShowCommodities         = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u0438 \\u0432\\u0441\\u0456 \\u043F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442\\u0438\nButton.ShowEmptyAccounts       = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438 \\u0437 \\u043D\\u0443\\u043B\\u044C\\u043E\\u0432\\u0438\\u043C \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u043C\nButton.ShowPercentValues       = Show Percent Values\nButton.ShowTimestamp           = Show Timestamp\nButton.Splits                  = \\u0421\\u043A\\u043B\\u0430\\u0434\\u0430\\u0454\\u0442\\u044C\\u0441\\u044F \\u0437\nButton.Start                   = Start\nButton.Stop                    = \\u0417\\u0443\\u043F\\u0438\\u043D\\u0438\\u0442\\u0438\nButton.SubstanceAnimations     = Enable Substance Look and Feel Animations\nButton.SumColVis               = Show Summary Columns\nButton.SumRowVis               = Show Summary Rows\nButton.ThemesEnabled           = \\u0412\\u0432\\u0456\\u043C\\u043A\\u043D\\u0443\\u0442\\u0438 \\u0456\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u043D\\u0456 \\u0442\\u0435\\u043C\\u0438\nButton.Timestamp               = \\u0412\\u0456\\u0434\\u043C\\u0456\\u0442\\u043A\\u0430 \\u043F\\u0440\\u043E \\u0447\\u0430\\u0441/\\u0434\\u0430\\u0442\\u0443\nButton.Today                   = \\u0421\\u044C\\u043E\\u0433\\u043E\\u0434\\u043D\\u0456\nButton.UpdateCurrenciesStartup = \\u041E\\u043D\\u043E\\u0432\\u0438\\u0442\\u0438 \\u043A\\u0443\\u0440\\u0441\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442 \\u043F\\u0440\\u0438 \\u0441\\u0442\\u0430\\u0440\\u0442\\u0456\nButton.UpdateOnline            = \\u041E\\u0442\\u0440\\u0438\\u043C\\u0430\\u0442\\u0438 \\u0437 \\u043C\\u0435\\u0440\\u0435\\u0436\\u0456\nButton.UpdateSecuritiesStartup = \\u041E\\u043D\\u043E\\u0432\\u0438\\u0442\\u0438 \\u0446\\u0456\\u043D\\u043D\\u0456 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0438 \\u043F\\u0440\\u0438 \\u0441\\u0442\\u0430\\u0440\\u0442\\u0456\nButton.UseDailyRate            = \\u0412\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u043F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0438\\u0439 \\u043A\\u0443\\u0440\\u0441 \\u0437\\u0430 \\u0434\\u0435\\u043D\\u044C\nButton.UseEncryption           = \\u0412\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0448\\u0438\\u0444\\u0440\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nButton.UseFuzzyMatch           = Use fuzzy match\nButton.UseLongNames            = \\u041F\\u043E\\u0432\\u043D\\u0456 \\u0456\\u043C\\u0435\\u043D\\u0430 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\nButton.UseProxy                = \\u0412\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u043F\\u0440\\u043E\\u043A\\u0441\\u0456\nButton.UseRegexForFilter       = \\u0412\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0440\\u0435\\u0433\\u0443\\u043B\\u044F\\u0440\\u043D\\u0456 \\u0432\\u0438\\u0440\\u0430\\u0437\\u0438 \\u0434\\u043B\\u044F \\u0444\\u0456\\u043B\\u044C\\u0442\\u0440\\u0456\\u0432\nButton.Vertical                = \\u0412\\u0435\\u0440\\u0442\\u0438\\u043A\\u0430\\u043B\\u044C\\u043D\\u0438\\u0439\nButton.Yes                     = \\u0422\\u0430\\u043A\nButton.Zoom                    = \\u0414\\u0435\\u0442\\u0430\\u043B\\u044C\\u043D\\u0456\\u0448\\u0435\n\nColumn.Account                        = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nColumn.AccountName                    = \\u041D\\u0430\\u0437\\u0432\\u0430 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443\nColumn.Action                         = \\u0414\\u0456\\u044F\nColumn.Actual                         = Actual\nColumn.Amount                         = \\u0421\\u0443\\u043C\\u0430\nColumn.Approve                        = \\u041F\\u0456\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0436\\u0435\\u043D\\u043E\nColumn.Balance                        = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\nColumn.Budgeted                       = Budgeted\nColumn.Charge                         = \\u0412\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438\nColumn.Close                          = Close\nColumn.Clr                            = \\u0423\\u0437\\u0433\nColumn.Code                           = Code\nColumn.Commodity                      = \\u041F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442\nColumn.CostBasis                      = \\u0411\\u0430\\u0437\\u043E\\u0432\\u0430 \\u0432\\u0430\\u0440\\u0442\\u0456\\u0441\\u0442\\u044C\nColumn.Credit                         = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\nColumn.Currency                       = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430\nColumn.Date                           = \\u0414\\u0430\\u0442\\u0430\nColumn.Day                            = \\u0414\\u0435\\u043D\\u044C\nColumn.Debit                          = \\u0414\\u0435\\u0431\\u0435\\u0442\nColumn.Decrease                       = \\u0417\\u043C\\u0435\\u043D\\u0448\\u0435\\u043D\\u043D\\u044F\nColumn.Deposit                        = \\u0414\\u0435\\u043F\\u043E\\u0437\\u0438\\u0442\nColumn.Description                    = \\u041E\\u043F\\u0438\\u0441\nColumn.Due                            = Due\nColumn.Enabled                        = \\u0410\\u043A\\u0442\\u0438\\u0432\\u043D\\u043E\nColumn.Entries                        = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nColumn.Event                          = Event\nColumn.ExchangeRate                   = \\u041A\\u0443\\u0440\\u0441\nColumn.Expense                        = \\u0412\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438\nColumn.Freq                           = \\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456\\u0441\\u0442\\u044C\nColumn.Gain                           = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043E\\u043A\nColumn.High                           = \\u0412\\u0438\\u0441\\u043E\\u043A\\u0438\\u0439\nColumn.Income                         = \\u0414\\u043E\\u0445\\u0456\\u0434\nColumn.Increase                       = \\u0417\\u0431\\u0456\\u043B\\u044C\\u0448\\u0435\\u043D\\u043D\\u044F\nColumn.Investment                     = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u044F\nColumn.LastPosted                     = Last Posted\nColumn.Loss                           = \\u0417\\u0431\\u0438\\u0442\\u043E\\u043A\nColumn.Low                            = \\u041D\\u0438\\u0437\\u044C\\u043A\\u0438\\u0439\nColumn.Memo                           = \\u041F\\u0430\\u043C'\\u044F\\u0442\\u043A\\u0430\nColumn.MktValue                       = \\u0420\\u0438\\u043D\\u043A\\u043E\\u0432\\u0430 \\u0432\\u0430\\u0440\\u0442\\u0456\\u0441\\u0442\\u044C\nColumn.Month                          = \\u041C\\u0456\\u0441\\u044F\\u0446\\u044C\nColumn.Num                            = \\u041D\\u043E\\u043C\\u0435\\u0440\nColumn.Payee                          = \\u041E\\u0442\\u0440\\u0438\\u043C\\u0443\\u0432\\u0430\\u0447\nColumn.Payment                        = \\u041F\\u043B\\u0430\\u0442\\u0456\\u0436\nColumn.Percentile                     = Percentile\nColumn.Period                         = Period\nColumn.Price                          = \\u0426\\u0456\\u043D\\u0430\nColumn.Print                          = \\u0414\\u0440\\u0443\\u043A\\u0443\\u0432\\u0430\\u0442\\u0438\nColumn.PropName                       = Property Name\nColumn.PropVal                        = Property Value\nColumn.Quantity                       = \\u041A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C\nColumn.Rebate                         = \\u0417\\u043D\\u0438\\u0436\\u043A\\u0430\nColumn.Receive                        = \\u0414\\u043E\\u0445\\u0456\\u0434\nColumn.ReconciledBalance              = \\u0423\\u0437\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u0438\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\nColumn.Remaining                      = Remaining\nColumn.Script                         = Script\nColumn.Security                       = \\u0426\\u0456\\u043D\\u043D\\u0438\\u0439 \\u043F\\u0430\\u043F\\u0456\\u0440\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = \\u041A-\\u0442\\u044C\nColumn.Short.RealizedGain             = \\u0420. \\u043F\\u0440\\u0438\\u0431.\nColumn.Short.RealizedGainPercentage   = \\u0420. \\u043F\\u0440\\u0438\\u0431. %\nColumn.Short.TotalGain                = \\u0412. \\u043F\\u0440\\u0438\\u0431.\nColumn.Short.TotalGainPercentage      = \\u0412. \\u043F\\u0440\\u0438\\u0431. %\nColumn.Short.UnrealizedGain           = \\u041D. \\u041F\\u0440\\u0438\\u0431.\nColumn.Short.UnrealizedGainPercentage = \\u041D. \\u043F\\u0440\\u0438\\u0431. %\nColumn.Spend                          = \\u0412\\u0438\\u0442\\u0440\\u0430\\u0442\\u0430\nColumn.Timestamp                      = \\u0432\\u0456\\u0434\\u043C\\u0456\\u0442\\u043A\\u0430\nColumn.Total                          = \\u0412\\u0441\\u044C\\u043E\\u0433\\u043E\nColumn.TotalCostBasis                 = \\u0412\\u0441\\u044C\\u043E\\u0433\\u043E \\u043F\\u0435\\u0440\\u0432\\u0456\\u0441\\u043D\\u0430 \\u0432\\u0430\\u0440\\u0442\\u0456\\u0441\\u0442\\u044C\nColumn.Type                           = \\u0422\\u0438\\u043F\nColumn.Value                          = Value\nColumn.Volume                         = \\u041E\\u0431'\\u0454\\u043C\nColumn.Withdrawal                     = \\u0417\\u043D\\u044F\\u0442\\u0442\\u044F\n\nDataStoreType.Bxds = \\u0434\\u0432\\u0456\\u0439\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B\nDataStoreType.H2   = H2 Relational Database\nDataStoreType.HSQL = HyperSQL Relational Database\nDataStoreType.XML  = XML \\u0444\\u0430\\u0439\\u043B\n\nItem.Amount         = \\u0421\\u0443\\u043C\\u0430\nItem.CashDeposit    = \\u0412\\u043D\\u0435\\u0441\\u0435\\u043D\\u043D\\u044F \\u0433\\u043E\\u0442\\u0456\\u0432\\u043A\\u0438\nItem.CashWithdrawal = \\u0417\\u043D\\u044F\\u0442\\u0442\\u044F \\u0433\\u043E\\u0442\\u0456\\u0432\\u043A\\u0438\nItem.Date           = \\u0414\\u0430\\u0442\\u0430\nItem.EFT            = \\u0415\\u043B\\u0435\\u043A\\u0442\\u0440\\u043E\\u043D\\u043D\\u0438\\u0439 \\u043F\\u0435\\u0440\\u0435\\u043A\\u0430\\u0437\nItem.Memo           = \\u041F\\u0430\\u043C'\\u044F\\u0442\\u043A\\u0438\nItem.NextNum        = \\u041D\\u0430\\u0441\\u0442\\u0443\\u043F\\u043D\\u0438\\u0439 \\u2116\nItem.Payee          = \\u041E\\u0442\\u0440\\u0438\\u043C\\u0443\\u0432\\u0430\\u0447\nItem.Trans          = \\u041E\\u043F\\u0435\\u0440.\n\nLabel.AccentColor         = Accent \\u041A\\u043E\\u043B\\u0456\\u0440:\nLabel.Account             = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A:\nLabel.AccountCode         = \\u041A\\u043E\\u0434 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443:\nLabel.AccountNumber       = \\u041D\\u043E\\u043C\\u0435\\u0440 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443:\nLabel.AccountOptions      = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443:\nLabel.AccountSeparator    = \\u0420\\u043E\\u0437\\u0434\\u0456\\u043B\\u044C\\u043D\\u0438\\u043A \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443:\nLabel.AccountStatus       = \\u0421\\u0442\\u0430\\u043D \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443:\nLabel.AccountType         = \\u0422\\u0438\\u043F \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443:\nLabel.Action              = \\u0414\\u0456\\u044F:\nLabel.Amount              = \\u0421\\u0443\\u043C\\u0430:\nLabel.AnIntRate           = \\u0420\\u0456\\u0447\\u043D\\u0430 \\u043F\\u0440\\u043E\\u0446\\u0435\\u043D\\u0442\\u043D\\u0430 \\u0441\\u0442\\u0430\\u0432\\u043A\\u0430:\nLabel.Available           = \\u0414\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u0438\\u0439:\nLabel.Balance             = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.BankAccount         = \\u0411\\u0430\\u043D\\u043A\\u0456\\u0432\\u0441\\u044C\\u043A\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = \\u0411\\u0430\\u0437\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A:\nLabel.BaseColor           = \\u0411\\u0430\\u0437\\u043E\\u0432\\u0438\\u0439 \\u043A\\u043E\\u043B\\u0456\\u0440:\nLabel.Bottom              = Bottom:\nLabel.By                  = \\u041F\\u043E:\nLabel.CashBalance         = \\u0413\\u043E\\u0442\\u0456\\u0432\\u043A\\u0430 (\\u0431\\u0430\\u043B\\u0430\\u043D\\u0441):\nLabel.Close               = Close:\nLabel.Color=Color:\nLabel.Commodity           = \\u041F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442:\nLabel.CompDaysPerYear     = \\u0421\\u043A\\u043B\\u0430\\u0434\\u0435\\u043D\\u0438\\u0445 \\u0434\\u043D\\u0456\\u0432 \\u0432 \\u0440\\u043E\\u0446\\u0456:\nLabel.CompPerTerm         = \\u0421\\u043A\\u043B\\u0430\\u0434\\u0435\\u043D\\u0438\\u0445 \\u043F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0456\\u0432 \\u0432 \\u0440\\u043E\\u0446\\u0456:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = Confirm Password:\nLabel.ConnTimeout         = Connection Timeout:\nLabel.Count               = \\u041A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C:\nLabel.CreateCurr          = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u0456\\u0448\\u043D\\u0443 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443:\nLabel.CssFiles            = CSS Files\nLabel.CsvFiles            = Csv Files\nLabel.Curr/Comm           = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 / \\u041F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442:\nLabel.Currencies          = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0438:\nLabel.Currency            = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430:\nLabel.Current             = \\u041F\\u043E\\u0442\\u043E\\u0447\\u043D\\u0438\\u0439:\nLabel.DatabaseBackend     = \\u0422\\u0438\\u043F \\u0411\\u0414:\nLabel.DatabaseName        = \\u0420\\u043E\\u0437\\u043C\\u0456\\u0449\\u0435\\u043D\\u043D\\u044F \\u0411\\u0414:\nLabel.DatabaseServer      = \\u0421\\u0435\\u0440\\u0432\\u0435\\u0440 \\u0411\\u0414:\nLabel.Date                = \\u0414\\u0430\\u0442\\u0430:\nLabel.DateFormat          = \\u0424\\u043E\\u0440\\u043C\\u0430\\u0442 \\u0434\\u0430\\u0442\\u0438:\nLabel.DaysPastDue         = \\u041F\\u0440\\u043E\\u0441\\u0442\\u0440\\u043E\\u0447\\u0435\\u043D\\u043E \\u0434\\u043D\\u0456\\u0432:\nLabel.DefaultCurrency     = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C:\nLabel.DelaySec            = \\u0417\\u0430\\u0442\\u0440\\u0438\\u043C\\u043A\\u0430 (\\u0441\\u0435\\u043A\\u0443\\u043D\\u0434)\nLabel.Description         = \\u041E\\u043F\\u0438\\u0441:\nLabel.DestAccount         = \\u041A\\u043E\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0443\\u044E\\u0447\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A:\nLabel.Difference          = \\u0420\\u0456\\u0437\\u043D\\u0438\\u0446\\u044F:\nLabel.Dividend            = \\u0414\\u0438\\u0432\\u0456\\u0434\\u0435\\u043D\\u0434:\nLabel.EndDate             = \\u041A\\u0456\\u043D\\u0446\\u0435\\u0432\\u0430 \\u0434\\u0430\\u0442\\u0430:\nLabel.EndOn               = \\u0417\\u0430\\u043A\\u0456\\u043D\\u0447\\u0443\\u0454\\u0442\\u044C\\u0441\\u044F:\nLabel.EndRow              = End Row:\nLabel.EndingBalance       = \\u041A\\u0456\\u043D\\u0446\\u0435\\u0432\\u0438\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.EquityAccount       = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u043F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u043E\\u0433\\u043E \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B\\u0443:\nLabel.EscrowPmi           = \\u041D\\u0430 \\u0437\\u0431\\u0435\\u0440\\u0435\\u0436\\u0435\\u043D\\u043D\\u0456 (\\u0432 \\u0431\\u0430\\u043D\\u043A\\u0443):\nLabel.Event               = Event:\nLabel.Every               = \\u041A\\u043E\\u0436\\u043D\\u0456:\nLabel.ExchangeAmount      = \\u0421\\u0443\\u043C\\u0430 \\u043E\\u0431\\u043C\\u0456\\u043D\\u0443:\nLabel.ExchangeRate        = \\u041A\\u0443\\u0440\\u0441:\nLabel.ExchangedAmount     = \\u0421\\u0443\\u043C\\u0430 \\u043F\\u0456\\u0441\\u043B\\u044F \\u043E\\u0431\\u043C\\u0456\\u043D\\u0443:\nLabel.Fees                = \\u0413\\u0440\\u043E\\u0448\\u043E\\u0432\\u0456 \\u0437\\u0431\\u043E\\u0440\\u0438:\nLabel.FeesAccount         = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0433\\u0440\\u043E\\u0448\\u043E\\u0432\\u0438\\u0445 \\u0437\\u0431\\u043E\\u0440\\u0456\\u0432:\nLabel.FileName            = \\u0406\\u043C'\\u044F \\u0444\\u0430\\u0439\\u043B\\u0443:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = \\u0414\\u0430\\u0442\\u0430 \\u043F\\u0435\\u0440\\u0448\\u043E\\u0433\\u043E \\u043F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0443:\nLabel.FocusColor          = Focus \\u041A\\u043E\\u043B\\u0456\\u0440:\nLabel.Format              = Format:\nLabel.Frequency           = \\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456\\u0441\\u0442\\u044C:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043E\\u043A:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = \\u0412\\u0438\\u0441\\u043E\\u0442\\u0430:\nLabel.High                = \\u0412\\u0435\\u0440\\u0445\\u043D\\u0456\\u0439:\nLabel.Host                = Host:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A:\nLabel.InterestAccount     = \\u0412\\u0456\\u0434\\u0441\\u043E\\u0442\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A:\nLabel.LastOccurrence      = \\u041E\\u0441\\u0442\\u0430\\u043D\\u043D\\u0456\\u0439 \\u0440\\u0430\\u0437:\nLabel.Layout              = \\u0420\\u043E\\u0437\\u043C\\u0456\\u0449\\u0435\\u043D\\u043D\\u044F:\nLabel.Left                = Left:\nLabel.LoanTerm            = \\u0421\\u0442\\u0440\\u043E\\u043A \\u0437\\u0430\\u0439\\u043C\\u0443 (\\u041C\\u0456\\u0441\\u044F\\u0446\\u0456\\u0432):\nLabel.Low                 = \\u041D\\u0438\\u0436\\u043D\\u0456\\u0439:\nLabel.MarketValue         = \\u0420\\u0438\\u043D\\u043A\\u043E\\u0432\\u0430 \\u0432\\u0430\\u0440\\u0442\\u0456\\u0441\\u0442\\u044C:\nLabel.MaxBackupCount      = \\u041C\\u0430\\u043A\\u0441\\u0438\\u043C\\u0430\\u043B\\u044C\\u043D\\u0430 \\u043A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u0438\\u0445 \\u043A\\u043E\\u043F\\u0456\\u0439:\nLabel.Memo                = \\u041F\\u0430\\u043C'\\u044F\\u0442\\u043A\\u0438:\nLabel.Monospace           = \\u041C\\u043E\\u043D\\u043E\\u0448\\u0438\\u0440\\u0438\\u043D\\u043D\\u0438\\u0439:\nLabel.Name                = \\u041D\\u0430\\u0437\\u0432\\u0430:\nLabel.NetIncome           = \\u0427\\u0438\\u0441\\u0442\\u0438\\u0439 \\u043F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043E\\u043A:\nLabel.NewPassword         = New Password:\nLabel.NextPayDate         = \\u0414\\u0430\\u0442\\u0430 \\u043D\\u0430\\u0441\\u0442\\u0443\\u043F\\u043D\\u043E\\u0433\\u043E \\u043F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0443:\nLabel.Notes               = \\u041F\\u0440\\u0438\\u043C\\u0456\\u0442\\u043A\\u0438:\nLabel.NumTrans            = \\u041A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439:\nLabel.Number              = \\u041D\\u043E\\u043C\\u0435\\u0440:\nLabel.OfxFiles            = Ofx Files\nLabel.OpenStateDate       = \\u0414\\u0430\\u0442\\u0430 \\u043F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u043E\\u0433\\u043E \\u0441\\u0442\\u0430\\u043D\\u0443:\nLabel.OpeningBalance      = \\u041F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u0438\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.OrigLoanAmt         = \\u041F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u0430 \\u0441\\u0443\\u043C\\u0430 \\u043F\\u043E\\u0437\\u0438\\u043A\\u0438:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = \\u041F\\u0430\\u0440\\u043E\\u043B\\u044C:\nLabel.Path                = \\u0428\\u043B\\u044F\\u0445\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = \\u041F\\u043B\\u0430\\u0442\\u0435\\u0436\\u0456\\u0432 \\u0437\\u0430 \\u0440\\u0456\\u043A:\nLabel.Payee               = \\u041E\\u0442\\u0440\\u0438\\u043C\\u0443\\u0432\\u0430\\u0447:\nLabel.Period              = Period\nLabel.Port                = Port:\nLabel.Prefix              = \\u041F\\u0440\\u0435\\u0444\\u0456\\u043A\\u0441:\nLabel.Price               = \\u0426\\u0456\\u043D\\u0430:\nLabel.Proportional        = \\u041F\\u0440\\u043E\\u043F\\u043E\\u0440\\u0446\\u0456\\u0439\\u043D\\u0438\\u0439:\nLabel.Quantity            = \\u041A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C:\nLabel.QuoteSource         = Quote Source:\nLabel.ReceivingAccount    = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0434\\u043B\\u044F \\u043E\\u0442\\u0440\\u0438\\u043C\\u0430\\u043D\\u043D\\u044F:\nLabel.ReconciledBalance   = \\u0423\\u0437\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u0438\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.RemindLater         = \\u041D\\u0430\\u0433\\u0430\\u0434\\u0430\\u0442\\u0438 \\u043F\\u0456\\u0441\\u043B\\u044F:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = \\u041F\\u043E\\u0432\\u0442\\u043E\\u0440\\u044E\\u0432\\u0430\\u0442\\u0438:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = \\u0417\\u0430\\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u0456 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0438:\nLabel.Resolution          = Resolution:\nLabel.ReturnOfCapital     = \\u041F\\u043E\\u0432\\u0435\\u0440\\u043D\\u0435\\u043D\\u043D\\u044F \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B\\u0443:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = \\u041C\\u0430\\u0441\\u0448\\u0442\\u0430\\u0431:\nLabel.Securities          = \\u0426\\u0456\\u043D\\u043D\\u0456 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0438:\nLabel.Security            = \\u0426\\u0456\\u043D\\u043D\\u0438\\u0439 \\u043F\\u0430\\u043F\\u0435\\u0440:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u0438 \\u043F\\u043E\\u0440\\u043E\\u0436\\u043D\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = Spreadsheet Files\nLabel.StartDate           = \\u041F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u0430 \\u0434\\u0430\\u0442\\u0430:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = \\u041F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u0430 \\u043F\\u043E\\u0437\\u0438\\u0446\\u0456\\u044F:\nLabel.StartRow            = Start Row:\nLabel.StatementDate       = \\u0414\\u0430\\u0442\\u0430 \\u0432\\u0438\\u043F\\u0438\\u0441\\u043A\\u0438:\nLabel.StorageType         = \\u0422\\u0438\\u043F \\u0437\\u0431\\u0435\\u0440\\u0456\\u0433\\u0430\\u043D\\u043D\\u044F:\nLabel.Suffix              = \\u0421\\u0443\\u0444\\u0456\\u043A\\u0441:\nLabel.Symbol              = \\u0421\\u0438\\u043C\\u0432\\u043E\\u043B:\nLabel.TargetBalance       = \\u0420\\u0435\\u0437\\u0443\\u043B\\u044C\\u0442\\u0443\\u044E\\u0447\\u0438\\u0439 \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441:\nLabel.Top                 = Top:\nLabel.Total               = \\u0412\\u0441\\u044C\\u043E\\u0433\\u043E:\nLabel.Transaction         = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044F:\nLabel.TransferFrom        = \\u041F\\u0435\\u0440\\u0435\\u043A\\u0430\\u0437 \\u0437:\nLabel.TransferTo          = \\u041F\\u0435\\u0440\\u0435\\u043A\\u0430\\u0437 \\u043D\\u0430:\nLabel.Type                = \\u0422\\u0438\\u043F:\nLabel.Units               = Units:\nLabel.UserName            = \\u0406\\u043C'\\u044F \\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0430:\nLabel.Value               = Value:\nLabel.Verify              = \\u041F\\u043E\\u0432\\u0442\\u043E\\u0440\\u0456\\u0442\\u044C:\nLabel.VerifyPassword      = \\u041F\\u043E\\u0432\\u0442\\u043E\\u0440\\u0456\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C:\nLabel.Visible             = \\u0412\\u0456\\u0434\\u043E\\u0431\\u0440\\u0430\\u0436\\u0430\\u0454\\u0442\\u044C\\u0441\\u044F:\nLabel.Volume              = \\u041E\\u0431'\\u0454\\u043C:\nLabel.Width               = Width:\nLabel.XMLFiles            = \\u0424\\u0430\\u0439\\u043B\\u0438 XML\nLabel.Year                = Year:\nLabel.jGnashFiles         = \\u0424\\u0430\\u0439\\u043B\\u0438 jGnash\n\nMenu.About.Name                       = _\\u041F\\u0440\\u043E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u0443\nMenu.About.Tooltip                    = \\u0406\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0456\\u044F \\u043F\\u0440\\u043E jGnash\nMenu.Account.Name                     = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nMenu.AccountRegister.Name             = \\u0420\\u0435\\u0454\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439...\nMenu.AddRemoveCurrency.Name=_\\u0414\\u043E\\u0434\\u0430\\u0442\\u0438/\\u0432\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438\\u2026\nMenu.BackgroundCurrencyUpdate.Name    = \\u041E\\u043D\\u043E\\u0432\\u0438\\u0442\\u0438 \\u0432\\u0441\\u0456 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0438\nMenu.BackgroundCurrencyUpdate.Tooltip = \\u041E\\u043D\\u043E\\u0432\\u0438\\u0442\\u0438 \\u043A\\u0443\\u0440\\u0441\\u0438 \\u0432\\u0441\\u0456\\u0445 \\u0432\\u0430\\u043B\\u044E\\u0442 \\u0432 \\u0444\\u043E\\u043D\\u0456\nMenu.BackgroundSecurityUpdate.Name    = \\u041E\\u043D\\u043E\\u0432\\u0438\\u0442\\u0438 \\u0432\\u0441\\u0456 \\u0446\\u0456\\u043D\\u043D\\u0456 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0438\nMenu.BackgroundSecurityUpdate.Tooltip = \\u041E\\u043D\\u043E\\u0432\\u0438\\u0442\\u0456 \\u0446\\u0456\\u043D\\u0438 \\u0432\\u0441\\u0456\\u0445 \\u0446\\u0456\\u043D\\u043D\\u0438\\u0445 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0456\\u0432 \\u0432 \\u0444\\u043E\\u043D\\u0456\nMenu.BalanceSheet.Name                = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441...\nMenu.BaseColor.Name                   = Change Base Colors\\u2026\nMenu.BudgetManager.Name               = Budget Manager\\u2026\nMenu.ChangeCredentials.Name           = Change Database Password\\u2026\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = Charts\nMenu.Cleared.Name                     = \\u041E\\u0447\\u0438\\u0449\\u0435\\u043D\\u043E\nMenu.Close.Name                       = _\\u0417\\u0430\\u043A\\u0440\\u0438\\u0442\\u0438\nMenu.Close.Tooltip                    = \\u0417\\u0430\\u043A\\u0440\\u0438\\u0442\\u0438 \\u0430\\u043A\\u0442\\u0438\\u0432\\u043D\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B\nMenu.CloseAllWindows.Name             = \\u0417\\u0430\\u043A\\u0440\\u0438\\u0442\\u0438 \\u0432\\u0441\\u0456 \\u0432\\u0456\\u043A\\u043D\\u0430\nMenu.ConfigImportFilters.Name         = Configure Transaction Import Filters...\nMenu.Console.Name                     = \\u0412\\u0456\\u043A\\u043D\\u043E Java-\\u043A\\u043E\\u043D\\u0441\\u043E\\u043B\\u0456...\nMenu.Copy.Name                        = _\\u041A\\u043E\\u043F\\u0456\\u044E\\u0432\\u0430\\u0442\\u0438\nMenu.Copy.Tooltip                     = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043A\\u043E\\u043F\\u0456\\u044E \\u0432\\u0438\\u0434\\u0456\\u043B\\u0435\\u043D\\u043E\\u0433\\u043E \\u0435\\u043B\\u0435\\u043C\\u0435\\u043D\\u0442\\u0443\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = _\\u0412\\u0430\\u043B\\u044E\\u0442\\u0438\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = _\\u0417\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C...\nMenu.DefaultCurrency.Tooltip          = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C\nMenu.Delete.Name                      = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438\nMenu.Duplicate.Name                   = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043A\\u043E\\u043F\\u0456\\u044E\nMenu.Edit.Name                        = _\\u0420\\u0435\\u0434\\u0430\\u0433\\u0443\\u0432\\u0430\\u0442\\u0438\nMenu.EditTranNumList.Name             = \\u0420\\u0435\\u0434\\u0430\\u0433\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0441\\u043F\\u0438\\u0441\\u043E\\u043A \\u043D\\u043E\\u043C\\u0435\\u0440\\u0456\\u0432 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nMenu.Exit.Name                        = _\\u0412\\u0438\\u0445\\u0456\\u0434\nMenu.Exit.Tooltip                     = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0438 \\u0440\\u043E\\u0431\\u043E\\u0442\\u0443 jGnash\nMenu.Export.Name                      = _\\u0415\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\nMenu.ExportAccounts.Name              = \\u0415\\u043A\\u0441\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438...\nMenu.File.Archive                     = \\u0410\\u0440\\u0445\\u0456\\u0432\\u2026\nMenu.File.Name                        = _\\u0424\\u0430\\u0439\\u043B\nMenu.Filter.Name                      = _\\u0424\\u0456\\u043B\\u044C\\u0442\\u0440\\u0438\nMenu.FontSize.Name                    = Change Default Font Size\\u2026\nMenu.Help.Name                        = _\\u0414\\u043E\\u043F\\u043E\\u043C\\u043E\\u0433\\u0430\nMenu.Hide.Name                        = \\u0421\\u0445\\u043E\\u0432\\u0430\\u0442\\u0438\nMenu.HistoryChart.Name                = \\u0414\\u0456\\u0430\\u0433\\u0440\\u0430\\u043C\\u0430 \\u0456\\u0441\\u0442\\u043E\\u0440\\u0456\\u0457...\nMenu.HistoryCommodity.Name            = _\\u0406\\u0441\\u0442\\u043E\\u0440\\u0456\\u044F\\u2026\nMenu.HistoryImport.Name               = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442 \\u043F\\u043E\\u043F\\u0435\\u0440\\u0435\\u0434\\u043D\\u0456\\u0445 \\u0434\\u0430\\u043D\\u0438\\u0445...\nMenu.IEBarChart.Name                  = Income / Expense Bar Chart\\u2026\nMenu.IEPieChart.Name                  = \\u041A\\u0440\\u0443\\u0433\\u043E\\u0432\\u0430 \\u0434\\u0456\\u0430\\u0433\\u0440\\u0430\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u0438 \\u0442\\u0430 \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438\"...\nMenu.Import.Name                      = _\\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\nMenu.ImportAccounts.Name              = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\\u2026\nMenu.ImportAccounts.Tooltip           = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432 jGnash\nMenu.ImportJgnash.Name                = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442 jGnash\\u2026\nMenu.ImportJgnash.Tooltip             = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B\\u0438 jGnash 1.11.x\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B\\u0438 SWIFT MT940\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B\\u0438 OFX \\u0442\\u0430 QFX\nMenu.ImportQif.Name                   = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 _QIF...\nMenu.Jump.Name                        = \\u041A\\u043E\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0443\\u044E\\u0447\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nMenu.ListOfAccounts.Name              = _\\u0421\\u043F\\u0438\\u0441\\u043E\\u043A \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\\u2026\nMenu.Locale.Name                      = \\u041C\\u043E\\u0432\\u0430 \\u0456\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0443...\nMenu.LookAndFeel.Name                 = \\u0417\\u043E\\u0432\\u043D\\u0456\\u0448\\u043D\\u0456\\u0439 \\u0432\\u0438\\u0433\\u043B\\u044F\\u0434 (Look and Feel)\nMenu.MarkAs.Name                      = \\u041F\\u043E\\u043C\\u0456\\u0442\\u0438\\u0442\\u0438 \\u044F\\u043A\nMenu.Modify.Name                      = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438\nMenu.ModifyCommodity.Name             = _\\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 / \\u0420\\u0435\\u0434\\u0430\\u0433\\u0443\\u0432\\u0430\\u0442\\u0438...\nMenu.ModifyCurrency.Name              = _\\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438...\nMenu.ModifyExchangeRates.Name         = \\u0420\\u0435\\u0434\\u0430\\u0433\\u0443\\u0432\\u0430\\u0442\\u0438 \\u043A\\u0443\\u0440\\u0441\\u0438 \\u043E\\u0431\\u043C\\u0456\\u043D\\u0443...\nMenu.MonthBalance.Name                = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0437\\u0430 \\u043C\\u0456\\u0441\\u044F\\u0446\\u044C...\nMenu.MonthBalanceCompare.Name         = Monthly Balance Comparison\\u2026\nMenu.MonthEndBalance.Name             = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u043D\\u0430 \\u043A\\u0456\\u043D\\u0435\\u0446\\u044C \\u043C\\u0456\\u0441\\u044F\\u0446\\u044F...\nMenu.MonthEndBalanceCSV.Name          = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u043D\\u0430 \\u043A\\u0456\\u043D\\u0435\\u0446\\u044C \\u043C\\u0456\\u0441\\u044F\\u0446\\u044F (CSV)...\nMenu.NetWorth.Name                    = \\u0412\\u043B\\u0430\\u0441\\u043D\\u0438\\u0439 \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B...\nMenu.New.Name                         = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043D\\u043E\\u0432\\u0438\\u0439\\u2026\nMenu.New.Tooltip                      = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043D\\u043E\\u0432\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B \\u0434\\u0430\\u043D\\u0438\\u0445\nMenu.NewReminder.Name                 = Create new reminder\nMenu.Open.Name                        = _\\u0412\\u0456\\u0434\\u043A\\u0440\\u0438\\u0442\\u0438\\u2026\nMenu.Open.Tooltip                     = \\u0412\\u0456\\u0434\\u043A\\u0440\\u0438\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B\nMenu.Option.Name                      = _\\u041D\\u0430\\u043B\\u0430\\u0448\\u0442\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F...\nMenu.Option.Tooltip                   = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043D\\u0430\\u043B\\u0430\\u0448\\u0442\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u0438\nMenu.PackDatabase.Name                = Pack Database\\u2026\nMenu.PayeePieChart.Name               = \\u041A\\u0440\\u0443\\u0433\\u043E\\u0432\\u0430 \\u0434\\u0456\\u0430\\u0433\\u0440\\u0430\\u043C\\u0430 \"\\u0414\\u043E\\u0445\\u043E\\u0434\\u0438 \\u0442\\u0430 \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438 \\u0437\\u0430 \\u043E\\u0434\\u0435\\u0440\\u0436\\u0443\\u0432\\u0430\\u0447\\u0435\\u043C\"\\u2026\nMenu.PeriodicAccountBalance.Name      = \\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0430 \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443 ...\nMenu.Portfolio.Name                   = \\u041F\\u043E\\u0440\\u0442\\u0444\\u043E\\u043B\\u0456\\u043E...\nMenu.ProfitLoss.Name                  = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u0438 \\u0442\\u0430 \\u0437\\u0431\\u0438\\u0442\\u043A\\u0438...\nMenu.ProfitLossTXT.Name               = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u0438 \\u0442\\u0430 \\u0437\\u0431\\u0438\\u0442\\u043A\\u0438 (\\u0422\\u0435\\u043A\\u0441\\u0442)...\nMenu.Reconcile.Name                   = \\u0412\\u0438\\u0432\\u0456\\u0440\\u0438\\u0442\\u0438\nMenu.Reconciled.Name                  = \\u0412\\u0438\\u0432\\u0456\\u0440\\u0435\\u043D\\u0438\\u0439\nMenu.RecurringList.Name               = _\\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457...\nMenu.Register.Name                    = _\\u0420\\u0435\\u0454\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439...\nMenu.Reports.Name                     = _\\u0417\\u0432\\u0456\\u0442\\u0438\nMenu.RunJavaScript.Name               = \\u0412\\u0438\\u043A\\u043E\\u043D\\u0430\\u0442\\u0438 JavaScript...\nMenu.RunJavaScript.Tooltip            = \\u0412\\u0438\\u043A\\u043E\\u043D\\u0430\\u0442\\u0438 JavaScript\nMenu.Save.Name                        = _\\u0417\\u0431\\u0435\\u0440\\u0456\\u0433\\u0442\\u0438\nMenu.Save.Tooltip                     = \\u0417\\u0431\\u0435\\u0440\\u0456\\u0433\\u0442\\u0438 \\u043F\\u043E\\u0442\\u043E\\u0447\\u043D\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B\nMenu.SaveAs.Name                      = \\u0417\\u0431\\u0435\\u0440\\u0456\\u0433\\u0442\\u0438 \\u044F\\u043A...\nMenu.SaveAs.Tooltip                   = \\u0417\\u0431\\u0435\\u0440\\u0456\\u0433\\u0442\\u0438 \\u043F\\u043E\\u0442\\u043E\\u0447\\u043D\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B \\u043F\\u0456\\u0434 \\u043D\\u043E\\u0432\\u0438\\u043C \\u0456\\u043C\\u0435\\u043D\\u0435\\u043C\nMenu.Securities.Name                  = \\u0426\\u0456\\u043D\\u043D\\u0456 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0438\nMenu.SetPassword.Name                 = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C...\nMenu.Show.Name                        = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u0438\nMenu.ShutdownServer.Name              = Shutdown Server\\u2026\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = \\u0406\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u043D\\u0456 \\u0442\\u0435\\u043C\\u0438\nMenu.Tools.Name                       = _\\u0421\\u0435\\u0440\\u0432\\u0456\\u0441\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = \\u041D\\u0435\\u0432\\u0438\\u0432\\u0456\\u0440\\u0435\\u043D\\u0438\\u0439\nMenu.View.Name                        = _\\u0412\\u0438\\u0433\\u043B\\u044F\\u0434\nMenu.Window.Name                      = _\\u0412\\u0456\\u043A\\u043D\\u043E\n\nMessage.AcceptLicense                = \\u042F \\u043F\\u0440\\u043E\\u0447\\u0438\\u0442\\u0430\\u0432(\\u043B\\u0430), \\u0437\\u0440\\u043E\\u0437\\u0443\\u043C\\u0456\\u0432(\\u043B\\u0430) \\u0442\\u0430 \\u043F\\u0440\\u0438\\u0439\\u043C\\u0430\\u044E \\u0443\\u043C\\u043E\\u0432\\u0438 \\u043B\\u0456\\u0446\\u0435\\u043D\\u0437\\u0456\\u0439.\nMessage.AccountAdd                   = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0434\\u043E\\u0434\\u0430\\u043D\\u043E\nMessage.AccountCode                  = \\u041A\\u043E\\u0434 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443 \\u043D\\u0435 \\u0443\\u043D\\u0456\\u043A\\u0430\\u043B\\u044C\\u043D\\u0438\\u0439, \\u0442\\u043E\\u043C\\u0443 \\u043A\\u043E\\u0434 \\u043D\\u0435 \\u0437\\u043C\\u0456\\u043D\\u0435\\u043D\\u043E\nMessage.AccountLocked                = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u043E\nMessage.AccountModify                = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0437\\u043C\\u0456\\u043D\\u0435\\u043D\\u043E\nMessage.AccountMoveFailed            = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u043F\\u0435\\u0440\\u0435\\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nMessage.AccountRemove                = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0432\\u0438\\u0434\\u0430\\u043B\\u0435\\u043D\\u043E\nMessage.AntiAlias                    = \\u0412\\u0432\\u0456\\u043C\\u043A\\u043D\\u0443\\u0442\\u0438 \\u0437\\u0433\\u043B\\u0430\\u0434\\u0436\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F \\u0442\\u0435\\u043A\\u0441\\u0442\\u0443!\nMessage.AutoSaveOff                  = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u0444\\u0430\\u0439\\u043B\\u0443 \\u0432\\u0438\\u043C\\u043A\\u043D\\u0435\\u043D\\u043E\nMessage.AutoSaveOn                   = \\u0410\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u0444\\u0430\\u0439\\u043B\\u0443 \\u0432\\u0432\\u0456\\u043C\\u043A\\u043D\\u0435\\u043D\\u043E\nMessage.CSVFile                      = Comma delimited Files (*.csv)\nMessage.CheckRecurring               = \\u041F\\u0435\\u0440\\u0435\\u0432\\u0456\\u0440\\u043A\\u0430 \\u043D\\u043E\\u0432\\u0438\\u0445 \\u043F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0438\\u0445 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nMessage.ClosingFile                  = Closing File\nMessage.CollectingReportData         = \\u0417\\u0431\\u0438\\u0440\\u0430\\u043D\\u043D\\u044F \\u0434\\u0430\\u043D\\u0438\\u0445 \\u0434\\u043B\\u044F \\u0437\\u0432\\u0456\\u0442\\u0443\nMessage.CompilingReport              = \\u041A\\u043E\\u043C\\u043F\\u0456\\u043B\\u044F\\u0446\\u0456\\u044F \\u0437\\u0432\\u0456\\u0442\\u0443\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = Delete the selected budget?\nMessage.ConfirmMultipleBudgetDelete  = Delete the selected budgets?\nMessage.ConfirmMultipleTransDelete   = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0432\\u0438\\u0431\\u0440\\u0430\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457?\nMessage.ConfirmReminderDelete        = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0432\\u0438\\u0431\\u0440\\u0430\\u043D\\u0435 \\u043D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F \\u043F\\u0440\\u043E \\u043F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0443 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0432\\u0438\\u0431\\u0440\\u0430\\u043D\\u0443 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C \\u0437\\u043C\\u0456\\u043D\\u0435\\u043D\\u0430 \\u043D\\u0430:\nMessage.DownloadingX                 = Downloading {0}\nMessage.EngineStart                  = \\u0411\\u0430\\u0437\\u0443 \\u0434\\u0430\\u043D\\u0438\\u0445 \\u0456\\u043D\\u0456\\u0446\\u0456\\u0430\\u043B\\u0456\\u0437\\u043E\\u0432\\u0430\\u043D\\u043E\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0441\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nMessage.Error.AccountRemove          = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0432\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0434\\u043E\\u0434\\u0430\\u0442\\u0438 \\u043F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442\nMessage.Error.AddCurrency            = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0434\\u043E\\u0434\\u0430\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0456\\u0442\\u044C \\u0441\\u043F\\u043E\\u0447\\u0430\\u0442\\u043A\\u0443 \\u0431\\u0430\\u0437\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = \\u0414\\u0443\\u0431\\u043B\\u0456 \\u0437\\u0430\\u0431\\u043E\\u0440\\u043E\\u043D\\u0435\\u043D\\u0456\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = \\u0424\\u0430\\u0439\\u043B \\u043D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = \\u0423\\u043C\\u043E\\u0432\\u0438 \\u043B\\u0456\\u0446\\u0435\\u043D\\u0437\\u0456\\u0457 \\u043D\\u0435 \\u0431\\u0443\\u043B\\u0438 \\u043F\\u0440\\u0438\\u0439\\u043D\\u044F\\u0442\\u0456\nMessage.Error.LoadingFile            = \\u041F\\u043E\\u043C\\u0438\\u043B\\u043A\\u0430 \\u0437\\u0430\\u0432\\u0430\\u043D\\u0442\\u0430\\u0436\\u0435\\u043D\\u043D\\u044F \\u0444\\u0430\\u0439\\u043B\\u0430\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0437\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442\nMessage.Error.ModifyCurrency         = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0437\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = \\u041D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E \\u0436\\u043E\\u0434\\u043D\\u043E\\u0457 \\u0442\\u0440\\u0430\\u043D\\u0437\\u0430\\u043A\\u0446\\u0456\\u0457\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = \\u041F\\u0456\\u0434\\u043A\\u043B\\u044E\\u0447\\u0435\\u043D\\u043D\\u044F \\u0434\\u043E \\u0432\\u0456\\u0434\\u0434\\u0430\\u043B\\u0435\\u043D\\u043E\\u0433\\u043E \\u0441\\u0435\\u0440\\u0432\\u0435\\u0440\\u0443 \\u0440\\u043E\\u0437\\u0456\\u0440\\u0432\\u0430\\u043D\\u043E\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = \\u0424\\u0430\\u0439\\u043B \\u0437\\u0430\\u043A\\u0440\\u0438\\u0442\\u043E\nMessage.FileIsLocked                 = \\u0424\\u0430\\u0439\\u043B \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u043E \\u0456\\u043D\\u0448\\u043E\\u044E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u043E\\u044E\nMessage.FileLoadComplete             = File load complete\nMessage.FileNotValid                 = \\u0412\\u0438\\u0434\\u0456\\u043B\\u0435\\u043D\\u043E \\u043D\\u0435\\u0434\\u0456\\u0439\\u0441\\u043D\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = \\u0411\\u0443\\u0434\\u044C \\u043B\\u0430\\u0441\\u043A\\u0430 \\u0437\\u0430\\u0447\\u0435\\u043A\\u0430\\u0439\\u0442\\u0435, \\u0456\\u043C\\u043F\\u043E\\u0440\\u0442 \\u043C\\u043E\\u0436\\u0435 \\u0437\\u0430\\u0439\\u043D\\u044F\\u0442\\u0438 \\u0431\\u0430\\u0433\\u0430\\u0442\\u043E \\u0447\\u0430\\u0441\\u0443\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash u043Fu043Eu0442u0440u0435u0431u0443u0454 11 JVM u0430u0431u043E u043Du043Eu0432u0456u0448u0435\nMessage.LoadReportFail               = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0437\\u0430\\u0432\\u0430\\u043D\\u0442\\u0430\\u0436\\u0438\\u0442\\u0438 \\u0432\\u0438\\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u043D\\u044F \\u0437\\u0432\\u0456\\u0442\\u0443\nMessage.LoadingFile                  = Loading file\\u2026\nMessage.LocaleChange                 = \\u041C\\u043E\\u0432\\u0443 \\u0456\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0443 \\u0437\\u043C\\u0456\\u043D\\u0435\\u043D\\u043E \\u043D\\u0430:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = \\u041D\\u0435 \\u043F\\u043E\\u0432\\u0442\\u043E\\u0440\\u044E\\u0454\\u0442\\u044C\\u0441\\u044F\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = \\u0406\\u0441\\u043D\\u0443\\u044E\\u0447\\u0443 \\u0431\\u0430\\u0437\\u0443 \\u0434\\u0430\\u043D\\u0438\\u0445 \\u0431\\u0443\\u0434\\u0435 \\u043F\\u0435\\u0440\\u0435\\u043F\\u0438\\u0441\\u0430\\u043D\\u043E\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u043E\\u0431\\u0440\\u043E\\u0431\\u0438\\u0442\\u0438 \\u0432\\u0438\\u0437\\u043D\\u0430\\u0447\\u0435\\u043D\\u043D\\u044F \\u0437\\u0432\\u0456\\u0442\\u0443\nMessage.PleaseWait                   = \\u0417\\u0430\\u0447\\u0435\\u043A\\u0430\\u0439\\u0442\\u0435 \\u0431\\u0443\\u0434\\u044C \\u043B\\u0430\\u0441\\u043A\\u0430\nMessage.PrefFail                     = \\u0421\\u0442\\u0430\\u0440\\u0456 \\u043F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u0438 \\u043D\\u0435 \\u0437\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E\nMessage.PrepShutdown                 = \\u041F\\u0456\\u0434\\u0433\\u043E\\u0442\\u043E\\u0432\\u043A\\u0430 \\u0434\\u043E \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043D\\u044F \\u0440\\u043E\\u0431\\u043E\\u0442\\u0438\nMessage.ProcessingReportData         = \\u041E\\u0431\\u0440\\u043E\\u0431\\u043A\\u0430 \\u0434\\u0430\\u043D\\u043D\\u0438\\u0445 \\u0434\\u043B\\u044F \\u0437\\u0432\\u0456\\u0442\\u0443\nMessage.Proxy                        = \\u0412\\u0441\\u0442\\u0430\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u043D\\u044F http \\u043F\\u0440\\u043E\\u043A\\u0441\\u0456:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = \\u041C\\u043E\\u0434\\u0443\\u043B\\u0456 \\u0437\\u0432\\u0456\\u0442\\u0456\\u0432 \\u0443\\u0441\\u043F\\u0456\\u0448\\u043D\\u043E \\u0437\\u0430\\u0432\\u0430\\u043D\\u0442\\u0430\\u0436\\u0435\\u043D\\u043E\nMessage.ReportWait                   = \\u0417\\u0430\\u0447\\u0435\\u043A\\u0430\\u0439\\u0442\\u0435 \\u0431\\u0443\\u0434\\u044C \\u043B\\u0430\\u0441\\u043A\\u0430, \\u0437\\u0430\\u0432\\u0430\\u043D\\u0442\\u0430\\u0436\\u0443\\u044E\\u0442\\u044C\\u0441\\u044F \\u043C\\u043E\\u0434\\u0443\\u043B\\u0456 \\u0437\\u0432\\u0456\\u0442\\u0456\\u0432\nMessage.RestartLocale                = \\u041F\\u0435\\u0440\\u0435\\u0437\\u0430\\u043F\\u0443\\u0441\\u0442\\u0456\\u0442\\u044C \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u0443, \\u0449\\u043E\\u0431 \\u0437\\u043C\\u0456\\u043D\\u0438 \\u0432\\u0441\\u0442\\u0443\\u043F\\u0438\\u043B\\u0438 \\u0432 \\u0434\\u0456\\u044E\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = \\u041F\\u043E\\u0448\\u0443\\u043A, \\u0437\\u0430\\u0447\\u0435\\u043A\\u0430\\u0439\\u0442\\u0435 \\u0431\\u0443\\u0434\\u044C \\u043B\\u0430\\u0441\\u043A\\u0430\nMessage.Shutdown                     = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043D\\u044F \\u0440\\u043E\\u0431\\u043E\\u0442\\u0438\nMessage.StartEndDate                 = \\u0412\\u0432\\u0435\\u0434\\u0456\\u0442\\u044C \\u0434\\u0430\\u0442\\u0443 \\u043F\\u043E\\u0447\\u0430\\u0442\\u043A\\u0443 \\u0442\\u0430 \\u043A\\u0456\\u043D\\u0446\\u044F\nMessage.StoreBackup                  = \\u0417\\u0430\\u043F\\u0438\\u0441 \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u043E\\u0457 \\u043A\\u043E\\u043F\\u0456\\u0457 \\u0432 \\u0444\\u0430\\u0439\\u043B:\nMessage.StoreComplete                = \\u0417\\u0430\\u043F\\u0438\\u0441 \\u0444\\u0430\\u0439\\u043B\\u0443 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043E\nMessage.StoreWait                    = \\u0417\\u0430\\u0447\\u0435\\u043A\\u0430\\u0439\\u0442\\u0435, \\u0444\\u0430\\u0439\\u043B \\u0437\\u0431\\u0435\\u0440\\u0456\\u0433\\u0430\\u0454\\u0442\\u044C\\u0441\\u044F...\nMessage.TXTFile                      = \\u0422\\u0435\\u043A\\u0441\\u0442\\u043E\\u0432\\u0456 \\u0444\\u0430\\u0439\\u043B\\u0438 (*.txt)\nMessage.TransactionAccountLocked     = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0434\\u043E\\u0434\\u0430\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E: \\u043A\\u043E\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0443\\u044E\\u0447\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A (\\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438) \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u043E\nMessage.TransactionAdd               = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E \\u0434\\u043E\\u0434\\u0430\\u043D\\u043E\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E \\u0432\\u0438\\u0434\\u0430\\u043B\\u0435\\u043D\\u043E\nMessage.TransactionRemoveLocked      = \\u041D\\u0435\\u043C\\u043E\\u0436\\u043B\\u0438\\u0432\\u043E \\u0432\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E: \\u043A\\u043E\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0443\\u044E\\u0447\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A (\\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438) \\u0437\\u0430\\u0431\\u043B\\u043E\\u043A\\u043E\\u0432\\u0430\\u043D\\u043E\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = \\u0426\\u0456\\u043D\\u0443 \\u0434\\u043B\\u044F {0} \\u0437\\u043C\\u0456\\u043D\\u0435\\u043D\\u043E\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = \\u0412\\u0438 \\u0432\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0454\\u0442\\u0435 \\u0432\\u0435\\u0440\\u0441\\u0456\\u044E\nMessage.Warn.CommodityInUse          = \\u041F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442 \\u0432\\u0436\\u0435 \\u0432\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0454\\u0442\\u044C\\u0441\\u044F\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0430 \\u0432\\u0436\\u0435 \\u0432\\u0438\\u043A\\u043E\\u0440\\u0438\\u0441\\u0442\\u043E\\u0432\\u0443\\u0454\\u0442\\u044C\\u0441\\u044F\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = \\u0411\\u0430\\u043D\\u043A\\u043E\\u0432\\u0441\\u044C\\u043A\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nName.ExpenseAccounts = \\u0412\\u0438\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nName.IncomeAccounts  = \\u041F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u043E\\u0432\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nName.Root            = \\u041F\\u0435\\u0440\\u0435\\u043B\\u0456\\u043A \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = \\u0417 {0,date,long} \\u043F\\u043E {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = \\u0421\\u0442\\u043E\\u0440. {0} \\u0437 {1}\nPattern.QuarterOfYear  = Quarter {0,number,#} of {1,number,#}\nPattern.WeekOfYear     = Week {0,number,00} of {1,number,#}\n\nPeriod.10Min     = 10 \\u0445\\u0432\\u0438\\u043B\\u0438\\u043D\nPeriod.15Min     = 15 \\u0445\\u0432\\u0438\\u043B\\u0438\\u043D\nPeriod.1Day      = 1 \\u0434\\u0435\\u043D\\u044C\nPeriod.1Hr       = 1 \\u0433\\u043E\\u0434\\u0438\\u043D\\u0430\nPeriod.2Hr       = 2 \\u0433\\u043E\\u0434\\u0438\\u043D\\u0438\nPeriod.30Min     = 30 \\u0445\\u0432\\u0438\\u043B\\u0438\\u043D\nPeriod.5Min      = 5 \\u0445\\u0432\\u0438\\u043B\\u0438\\u043D\nPeriod.8Hr       = 8 \\u0433\\u043E\\u0434\\u0438\\u043D\nPeriod.BiWeekly  = Bi-Weekly\nPeriod.Daily     = \\u0429\\u043E\\u0434\\u0435\\u043D\\u043D\\u043E\nPeriod.Monthly   = \\u0429\\u043E\\u043C\\u0456\\u0441\\u044F\\u0446\\u044F\nPeriod.NextStart = \\u041F\\u0440\\u0438 \\u043D\\u0430\\u0441\\u0442\\u0443\\u043F\\u043D\\u043E\\u043C\\u0443 \\u0437\\u0430\\u043F\\u0443\\u0441\\u043A\\u0443\nPeriod.None      = \\u0416\\u043E\\u0434\\u0435\\u043D\nPeriod.OnlyOnce  = \\u0422\\u0456\\u043B\\u044C\\u043A\\u0438 \\u043E\\u0434\\u0438\\u043D \\u0440\\u0430\\u0437\nPeriod.Quarterly = Quarterly\nPeriod.Weekly    = \\u0429\\u043E\\u0442\\u0438\\u0436\\u043D\\u044F\nPeriod.Yearly    = \\u0429\\u043E\\u0440\\u043E\\u043A\\u0443\n\nQuestion.DeleteAttachment = This transaction has an attachment.\\n\\nDo you want to delete the attachment also?\n\nQuoteSource.None     = \\u0416\\u043E\\u0434\\u0435\\u043D\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = Dividend\nSecurityEvent.Price    = Price\nSecurityEvent.Split    = Split\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = Account Balance\nSortOrder.AccountName        = Account Name\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\nState.Reconciled    = R\n\nTab.About           = \\u041F\\u0440\\u043E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u0443\nTab.Accounts        = Accounts\nTab.Adjust          = \\u041A\\u043E\\u0440\\u0435\\u043A\\u0442\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTab.AppLicense      = jGnash License\nTab.Budgeting       = Budgeting\nTab.Charge          = \\u041F\\u043E\\u043F\\u043E\\u0432\\u043D\\u0435\\u043D\\u043D\\u044F\nTab.Credit          = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\nTab.Credits         = \\u0410\\u0432\\u0442\\u043E\\u0440\\u0438\nTab.DataEngine      = \\u0411\\u0430\\u0437\\u0430 \\u0434\\u0430\\u043D\\u0438\\u0445\nTab.DataProviders   = Data Providers\nTab.Day             = \\u0414\\u0435\\u043D\\u044C\nTab.Debit           = \\u0414\\u0435\\u0431\\u0435\\u0442\nTab.Formats         = Formats\nTab.GPLLicense      = \\u041B\\u0456\\u0446\\u0435\\u043D\\u0437\\u0456\\u044F GPL\nTab.General         = \\u0417\\u0430\\u0433\\u0430\\u043B\\u044C\\u043D\\u0456\nTab.LGPLLicense     = \\u041B\\u0456\\u0446\\u0435\\u043D\\u0437\\u0456\\u044F LGPL\nTab.License         = \\u041B\\u0456\\u0446\\u0435\\u043D\\u0437\\u0456\\u044F\nTab.Month           = \\u041C\\u0456\\u0441\\u044F\\u0446\\u044C\nTab.Network         = Network\nTab.None            = \\u0416\\u043E\\u0434\\u043D\\u043E\\u0433\\u043E\nTab.Payment         = \\u041F\\u043B\\u0430\\u0442\\u0456\\u0436\nTab.Register        = \\u0420\\u0435\\u0454\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nTab.Reminders       = \\u041D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTab.Report          = \\u0417\\u0432\\u0456\\u0442\nTab.StartupShutdown = \\u0417\\u0430\\u043F\\u0443\\u0441\\u043A / \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043D\\u044F \\u0440\\u043E\\u0431\\u043E\\u0442\\u0438\nTab.SysInfo         = \\u0421\\u0438\\u0441\\u0442\\u0435\\u043C\\u043D\\u0430 \\u0456\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0456\\u044F\nTab.Transfer        = \\u041F\\u0435\\u0440\\u0435\\u043A\\u0430\\u0437\nTab.Week            = \\u0422\\u0438\\u0436\\u0434\\u0435\\u043D\\u044C\nTab.Year            = \\u0420\\u0456\\u043A\n\nTag.Bank                   = \\u0411\\u0430\\u043D\\u043A\nTag.Dividend               = \\u0414\\u0438\\u0432\\u0456\\u0434\\u0435\\u043D\\u0434\nTag.FeesOffset             = Fees Offset\nTag.GainLoss               = \\u0414\\u043E\\u0445\\u0456\\u0434/(\\u0417\\u0431\\u0438\\u0442\\u043E\\u043A)\nTag.GainsOffset            = Gains Offset\nTag.Investment             = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0439\\u043D\\u0430 \\u043F\\u043B\\u0430\\u0442\\u0430\nTag.InvestmentCashTransfer = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0439\\u043D\\u0438\\u0439 \\u043F\\u0435\\u0440\\u0435\\u043A\\u0430\\u0437 \\u043A\\u043E\\u0448\\u0442\\u0456\\u0432\nTag.InvestmentFee          = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0439\\u043D\\u0430 \\u043F\\u043B\\u0430\\u0442\\u0430\nTag.LTCG                   = Long Term Capital Gains Distribution\nTag.MTCG                   = Mid Term Capital Gains Distribution\nTag.Misc                   = \\u0420\\u0456\\u0437\\u043D\\u0435\nTag.NonTaxableInterest     = \\u041D\\u0435\\u043E\\u043F\\u043E\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0443\\u0432\\u0430\\u043D\\u0438\\u0439 \\u0432\\u0456\\u0434\\u0441\\u043E\\u0442\\u043E\\u043A\nTag.STCG                   = Short Term Capital Gains Distribution\nTag.TaxableInterest        = \\u041E\\u043F\\u043E\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0443\\u0432\\u0430\\u043D\\u0438\\u0439 \\u0432\\u0456\\u0434\\u0441\\u043E\\u0442\\u043E\\u043A\nTag.Vat                    = Vat\n\nTitle.About                      = \\u041F\\u0440\\u043E \\u043F\\u0440\\u043E\\u0433\\u0440\\u0430\\u043C\\u0443\nTitle.AccountBalance             = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443\nTitle.AccountFilter              = \\u0424\\u0456\\u043B\\u044C\\u0442\\u0440\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\nTitle.AccountGroups              = Account Groups\nTitle.AccountInfo                = \\u0406\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0456\\u044F \\u043F\\u0440\\u043E \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nTitle.AccountRegister            = Account Register\nTitle.AccountSecurities          = \\u0420\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0446\\u0456\\u043D\\u043D\\u0438\\u0445 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0456\\u0432\nTitle.AddRemCurr                 = \\u0414\\u043E\\u0434\\u0430\\u0442\\u0438 / \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0438\nTitle.AmortizationSetup          = \\u041D\\u0430\\u0441\\u0442\\u0440\\u043E\\u0439\\u043A\\u0430 \\u0430\\u043C\\u043E\\u0440\\u0442\\u0438\\u0437\\u0430\\u0446\\u0456\\u0457\nTitle.Archive                    = \\u0410\\u0440\\u0445\\u0456\\u0432\nTitle.AutoComplete               = Auto Completion\nTitle.AutoSave                   = \\u0417\\u0431\\u0435\\u0440\\u0456\\u0433\\u0430\\u0442\\u0438 \\u0430\\u0432\\u0442\\u043E\\u043C\\u0430\\u0442\\u0438\\u0447\\u043D\\u043E\nTitle.Available                  = \\u0414\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u043E\nTitle.BackgroundUpdate           = \\u041E\\u043D\\u043E\\u0432\\u043B\\u0435\\u043D\\u043D\\u044F \\u0432 \\u0444\\u043E\\u043D\\u0456\nTitle.BackingStore               = \\u0414\\u043E\\u0434\\u0430\\u0442\\u043A\\u043E\\u0432\\u0435 \\u0441\\u0445\\u043E\\u0432\\u0438\\u0449\\u0435\nTitle.BalanceSheet               = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\\u043E\\u0432\\u0438\\u0439 \\u0437\\u0432\\u0456\\u0442\nTitle.BaseColor                  = Change Base Colors\nTitle.BudgetGoal                 = Budget Manager\nTitle.BudgetManager              = Budget Manager\nTitle.BudgetProperties           = Budget Properties\nTitle.ButtonOrder                = Button Order\nTitle.ChangePassword             = Change Password\nTitle.CheckDesign                = \\u0414\\u0438\\u0437\\u0430\\u0439\\u043D\\u0435\\u0440 \\u0447\\u0435\\u043A\\u0456\\u0432\nTitle.ChooseAccounts             = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u0434\\u043B\\u044F \\u0441\\u0442\\u0432\\u043E\\u0440\\u0435\\u043D\\u043D\\u044F\nTitle.ColVis                     = \\u0421\\u0442\\u043E\\u0432\\u043F\\u0447\\u043A\\u0438\\u043A \\u0432\\u0456\\u0434\\u043E\\u0431\\u0440\\u0430\\u0436\\u0430\\u0454\\u0442\\u044C\\u0441\\u044F\nTitle.Colors                     = \\u041A\\u043E\\u043B\\u044C\\u043E\\u0440\\u0438\nTitle.CommoditiesSecurities      = \\u0426\\u0456\\u043D\\u043D\\u0456 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0438\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = \\u041F\\u0456\\u0434\\u0442\\u0432\\u0435\\u0440\\u0434\\u0456\\u0442\\u044C\nTitle.ConnectServer              = Connect to Server\nTitle.Connection                 = Connection\nTitle.Console                    = \\u0412\\u0456\\u043A\\u043D\\u043E Java-\\u043A\\u043E\\u043D\\u0441\\u043E\\u043B\\u0456\nTitle.CreateModifyCommodities    = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 / \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442\\u0438\nTitle.Credits                    = \\u041A\\u0440\\u0435\\u0434\\u0438\\u0442\\u043E\\u0432\\u0456\nTitle.Currencies                 = \\u0412\\u0430\\u043B\\u044E\\u0442\\u0438\nTitle.Current                    = \\u041F\\u043E\\u0442\\u043E\\u0447\\u043D\\u0438\\u0439\nTitle.CutOffDate                 = \\u0414\\u0430\\u0442\\u0430 \\u0444\\u0456\\u043A\\u0441\\u043E\\u0432\\u0430\\u043D\\u043E\\u0433\\u043E \\u0431\\u0430\\u043B\\u0430\\u043D\\u0441\\u0443:\nTitle.DatabaseCfg                = \\u041A\\u043E\\u043D\\u0444\\u0456\\u0433\\u0443\\u0440\\u0430\\u0446\\u0456\\u044F \\u0411\\u0414\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = \\u0414\\u0435\\u0431\\u0435\\u0442\\u043E\\u0432\\u0456\nTitle.DefDefCurr                 = \\u0412\\u0438\\u0437\\u043D\\u0430\\u0447\\u0438\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C\nTitle.DefTranNum                 = \\u041D\\u043E\\u043C\\u0435\\u0440\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C\nTitle.DefaultBehavior            = \\u041F\\u043E\\u0432\\u0435\\u0434\\u0456\\u043D\\u043A\\u0430 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C\nTitle.Defaults                   = \\u0417\\u043D\\u0430\\u0447\\u0435\\u043D\\u043D\\u044F \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\\u043C\nTitle.DeleteAttachment           = Delete Attachment\nTitle.Display                    = \\u0417\\u043E\\u0432\\u043D\\u0456\\u0448\\u043D\\u0456\\u0439 \\u0432\\u0438\\u0433\\u043B\\u044F\\u0434\nTitle.DuplicateTransaction       = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043A\\u043E\\u043F\\u0456\\u044E \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nTitle.DuplicateTransactionsFound = \\u0417\\u043D\\u0430\\u0439\\u0434\\u0435\\u043D\\u043E \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E-\\u0434\\u0443\\u0431\\u043B\\u0456\\u043A\\u0430\\u0442\nTitle.EditExchangeRates          = \\u0420\\u0435\\u0434\\u0430\\u0433\\u0443\\u0432\\u0430\\u0442\\u0438 \\u043A\\u0443\\u0440\\u0441\\u0438 \\u043E\\u0431\\u043C\\u0456\\u043D\\u0443\nTitle.EndMonthBalance            = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443 \\u043D\\u0430 \\u043A\\u0456\\u043D\\u0435\\u0446\\u044C \\u043C\\u0456\\u0441\\u044F\\u0446\\u044F\nTitle.EnterPassword              = \\u0412\\u0432\\u0435\\u0434\\u0456\\u0442\\u044C \\u043F\\u0430\\u0440\\u043E\\u043B\\u044C\nTitle.Entry                      = \\u0417\\u0430\\u043F\\u0438\\u0441\nTitle.Error                      = \\u041F\\u043E\\u043C\\u0438\\u043B\\u043A\\u0430\nTitle.EventHistory               = Event History\nTitle.ExchangeRate               = \\u041A\\u0443\\u0440\\u0441 \\u043E\\u0431\\u043C\\u0456\\u043D\\u0443\nTitle.FileImport                 = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B \\u0434\\u043B\\u044F \\u0456\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = \\u0444\\u0456\\u043B\\u044C\\u0442\\u0440\\u0438\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = \\u0428\\u0440\\u0438\\u0444\\u0442\\u0438\nTitle.Frequency                  = \\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456\\u0441\\u0442\\u044C\nTitle.HTTPProxy                  = \\u041F\\u0440\\u043E\\u043A\\u0441\\u0456 HTTP\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442 \\u043F\\u043E\\u043F\\u0435\\u0440\\u0435\\u0434\\u043D\\u0456\\u0445 \\u0434\\u0430\\u043D\\u0438\\u0445\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0447\\u0430\\u0441\\u0442\\u0438\\u043D\\u0443 QIF-\\u0444\\u0430\\u0439\\u043B\\u0443\nTitle.ImpSum                     = \\u0420\\u0435\\u0437\\u044E\\u043C\\u0435 \\u0456\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\nTitle.ImportOFX                  = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 OFX-\\u0444\\u0430\\u0439\\u043B\nTitle.ImportTransactions         = \\u0406\\u043C\\u043F\\u043E\\u0440\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0442\\u0440\\u0430\\u043D\\u0437\\u0430\\u043A\\u0446\\u0456\\u0457 \\u0437 \\u0444\\u0430\\u0439\\u043B\\u0443\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = \\u0456\\u043D\\u0444\\u043E\\u0440\\u043C\\u0430\\u0446\\u0456\\u044F\nTitle.InvFees                    = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0439\\u043D\\u0456 \\u0442\\u0430\\u0440\\u0438\\u0444\\u0438\nTitle.InvGainsLoss               = \\u0406\\u043D\\u0432\\u0435\\u0441\\u0442\\u0438\\u0446\\u0456\\u0439\\u043D\\u0456 \\u0434\\u043E\\u0445\\u043E\\u0434\\u0438 / \\u0437\\u0431\\u0438\\u0442\\u043A\\u0438\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nTitle.ModOFXTrans                = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457 OFX\nTitle.ModQIFTrans                = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457 QIF\nTitle.ModifyAccount              = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nTitle.ModifyCurrencies           = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0438\nTitle.ModifyReminder             = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTitle.ModifySecHistory           = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0456\\u0441\\u0442\\u043E\\u0440\\u0456\\u044E \\u0446\\u0456\\u043D\\u043D\\u0438\\u0445 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0456\\u0432\nTitle.ModifyTransaction          = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = \\u041D\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nTitle.NewBudget                  = \\u043D\\u043E\\u0432\\u0438\\u0439 \\u0431\\u044E\\u0434\\u0436\\u0435\\u0442\nTitle.NewFile                    = \\u0421\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043D\\u043E\\u0432\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043B\nTitle.NewPassword                = New Password\nTitle.NewReminder                = \\u041D\\u043E\\u0432\\u0435 \\u043D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTitle.NewTrans                   = \\u041D\\u043E\\u0432\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044F\nTitle.Notes                      = \\u0417\\u0430\\u043C\\u0456\\u0442\\u043A\\u0438\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = \\u0412\\u0456\\u0434\\u043A\\u0440\\u0438\\u0442\\u0438\nTitle.Options                    = \\u041D\\u0430\\u043B\\u0430\\u0448\\u0442\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u0438 \\u0441\\u0442\\u043E\\u0440\\u0456\\u043D\\u043A\\u0438\nTitle.ParentAccount              = \\u0404 \\u0441\\u0443\\u0431\\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u043E\\u043C \\u0434\\u043B\\u044F \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443\nTitle.PercentDist                = \\u0412\\u0456\\u0434\\u0441\\u043E\\u0442\\u043A\\u043E\\u0432\\u0435 \\u0440\\u043E\\u0437\\u043F\\u043E\\u0434\\u0456\\u043B\\u0435\\u043D\\u043D\\u044F\nTitle.PercentExpense             = \\u0412\\u0456\\u0434\\u0441\\u043E\\u0442\\u043E\\u043A \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442\nTitle.PercentIncome              = \\u0412\\u0456\\u0434\\u0441\\u043E\\u0442\\u043E\\u043A \\u0434\\u043E\\u0445\\u043E\\u0434\\u0456\\u0432\nTitle.PleaseWait                 = \\u0417\\u0430\\u0447\\u0435\\u043A\\u0430\\u0439\\u0442\\u0435 \\u0431\\u0443\\u0434\\u044C \\u043B\\u0430\\u0441\\u043A\\u0430\nTitle.PortfolioReport            = Portfolio Report\nTitle.PriceHistory               = Price History\nTitle.ProfitLoss                 = \\u0412\\u0456\\u0434\\u043E\\u043C\\u0456\\u0441\\u0442\\u044C \\u043F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043A\\u0456\\u0432 \\u0442\\u0430 \\u0437\\u0431\\u0438\\u0442\\u043A\\u0456\\u0432\nTitle.ReconcileSettings          = \\u041D\\u0430\\u043B\\u0430\\u0448\\u0442\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F \\u043F\\u043E\\u0433\\u043E\\u0434\\u0436\\u0435\\u043D\\u043D\\u044F \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\nTitle.Reminder                   = \\u041D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTitle.Reminders                  = \\u041D\\u0430\\u0433\\u0430\\u0434\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F\nTitle.RenameBudget               = Rename Budget\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = Retained Earnings\nTitle.ReverseAccountBalances     = Reverse Displayed Account Balances\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = \\u0417\\u0431\\u0435\\u0440\\u0435\\u0433\\u0442\\u0438 \\u044F\\u043A\nTitle.SaveFile                   = \\u0417\\u0431\\u0435\\u0440\\u0435\\u0433\\u0442\\u0438 \\u0444\\u0430\\u0439\\u043B\nTitle.SecurityHistory            = \\u0406\\u0441\\u0442\\u043E\\u0440\\u0456\\u044F \\u0446\\u0456\\u043D\\u043D\\u043E\\u0433\\u043E \\u043F\\u0430\\u043F\\u0435\\u0440\\u0443\nTitle.SelAccount                 = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nTitle.SelAvailCurr               = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0434\\u043E\\u0441\\u0442\\u0443\\u043F\\u043D\\u0456 \\u0432\\u0430\\u043B\\u044E\\u0442\\u0438\nTitle.SelDate                    = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0434\\u0430\\u0442\\u0443\nTitle.SelDefCurr                 = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0432\\u0430\\u043B\\u044E\\u0442\\u0443 \\u0437\\u0430 \\u0437\\u0430\\u043C\\u043E\\u0432\\u0447\\u0435\\u043D\\u043D\\u044F\\u043C\nTitle.SelDefLocale               = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u043C\\u043E\\u0432\\u0443 \\u0456\\u043D\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0443\nTitle.SelDestAccount             = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u043A\\u043E\\u0440\\u0435\\u0441\\u043F\\u043E\\u043D\\u0434\\u0443\\u044E\\u0447\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = \\u0412\\u0438\\u0431\\u0440\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A \\u043F\\u043E\\u0447\\u0430\\u0442\\u043A\\u043E\\u0432\\u043E\\u0433\\u043E \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B\\u0443\nTitle.SelFile                    = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0444\\u0430\\u0439\\u043B\nTitle.SelQifDateFormat           = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u0444\\u043E\\u0440\\u043C\\u0430\\u0442 \\u0434\\u0430\\u0442\\u0438 QIF\nTitle.SelectColor                = \\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044C \\u043A\\u043E\\u043B\\u044C\\u043E\\u0440\nTitle.Selected                   = \\u0412\\u0438\\u0431\\u0440\\u0430\\u043D\\u043E\nTitle.Shutdown                   = \\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043D\\u043D\\u044F \\u0440\\u043E\\u0431\\u043E\\u0442\\u0438\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = \\u0420\\u043E\\u0437\\u0434\\u0456\\u043B\\u0438\\u0442\\u0438 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044E\nTitle.Startup                    = Startup\nTitle.Steps                      = \\u0415\\u0442\\u0430\\u043F\\u0438\nTitle.Success                    = \\u0443\\u0441\\u043F\\u0456\\u0445\nTitle.Summary                    = \\u0420\\u0435\\u0437\\u044E\\u043C\\u0435\nTitle.TagManager=Tag Manager\nTitle.Terms                      = Terms\nTitle.Transaction                = \\u041E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044F\nTitle.TransactionImport          = Transaction Import\nTitle.TransactionList            = \\u0420\\u0435\\u0454\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nTitle.TransactionSetup           = \\u041D\\u0430\\u043B\\u0430\\u0448\\u0442\\u0443\\u0432\\u0430\\u043D\\u043D\\u044F \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = View Image\nTitle.Visible                    = \\u0412\\u0456\\u0434\\u043E\\u0431\\u0440\\u0430\\u0436\\u0430\\u0454\\u0442\\u044C\\u0441\\u044F\nTitle.VisibleAccountTypes        = Visible Account Types\nTitle.Warning                    = Warning\n\nToolTip.AccountList                          = \\u041F\\u0435\\u0440\\u0435\\u043B\\u0456\\u043A \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0456\\u0432\nToolTip.AccountRegister                      = \\u0420\\u0435\\u0454\\u0441\\u0442\\u0440 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439 \\u043F\\u043E \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = \\u041F\\u043E\\u043A\\u0430\\u0437\\u0430\\u0442\\u0438/\\u0441\\u0445\\u043E\\u0432\\u0430\\u0442\\u0438 \\u043A\\u043E\\u043B\\u043E\\u043D\\u043A\\u0443\nToolTip.ConvertSEntry                        = \\u041F\\u0435\\u0440\\u0435\\u0442\\u0432\\u043E\\u0440\\u0438\\u0442\\u0438 \\u043D\\u0430 \\u0442\\u0440\\u0430\\u043D\\u0437\\u0430\\u043A\\u0446\\u0456\\u044E \\u0437 \\u043F\\u043E\\u0434\\u0432\\u0456\\u0439\\u043D\\u0438\\u043C \\u0437\\u0430\\u043F\\u0438\\u0441\\u043E\\u043C\nToolTip.DeleteAccount                        = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = \\u0424\\u0456\\u043B\\u044C\\u0442\\u0440\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0438\nToolTip.FilterAccounts                       = \\u0424\\u0456\\u043B\\u044C\\u0442\\u0440\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0437\\u0430 \\u0442\\u0438\\u043F\\u043E\\u043C \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0440\\u043E\\u0437\\u043C\\u0456\\u0440 \\u0448\\u0440\\u0438\\u0444\\u0442\\u0443\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = Help\nToolTip.ISIN                                 = \\u041C\\u0456\\u0436\\u043D\\u0430\\u0440\\u043E\\u0434\\u043D\\u0438\\u0439 \\u0456\\u0434\\u0435\\u043D\\u0442\\u0438\\u0444\\u0456\\u043A\\u0430\\u0446\\u0456\\u0439\\u043D\\u0438\\u0439 \\u043D\\u043E\\u043C\\u0435\\u0440 \\u0446\\u0456\\u043D\\u043D\\u0438\\u0445 \\u043F\\u0430\\u043F\\u0435\\u0440\\u0456\\u0432\nToolTip.IntegersOnly                         = \\u0442\\u0456\\u043B\\u044C\\u043A\\u0438 \\u0446\\u0456\\u043B\\u0456 \\u0434\\u043E\\u0437\\u0432\\u043E\\u043B\\u0435\\u043D\\u043E\nToolTip.ModifyAccount                        = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nToolTip.NewAccount                           = \\u041D\\u043E\\u0432\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043E\\u043A\nToolTip.PageSetup                            = \\u041F\\u0430\\u0440\\u0430\\u043C\\u0435\\u0442\\u0440\\u0438 \\u0441\\u0442\\u043E\\u0440\\u0456\\u043D\\u043A\\u0438\nToolTip.PrintRegRep                          = \\u0414\\u0440\\u0443\\u043A\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0441\\u043F\\u0438\\u0441\\u043E\\u043A \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nToolTip.ReconcileAccount                     = \\u0412\\u0438\\u0432\\u0456\\u0440\\u043A\\u0430 \\u0440\\u0430\\u0445\\u0443\\u043D\\u043A\\u0443\nToolTip.Reminders                            = \\u041F\\u0435\\u0440\\u0456\\u043E\\u0434\\u0438\\u0447\\u043D\\u0456 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nToolTip.ResizeColumns                        = \\u0417\\u043C\\u0456\\u043D\\u0438\\u0442\\u0438 \\u0448\\u0438\\u0440\\u0438\\u043D\\u0443 \\u043A\\u043E\\u043B\\u043E\\u043D\\u043E\\u043A\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = \\u041A\\u0456\\u043B\\u044C\\u043A\\u0456\\u0441\\u0442\\u044C \\u0446\\u0438\\u0444\\u0440 \\u043F\\u0456\\u0441\\u043B\\u044F \\u043A\\u043E\\u043C\\u0438\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = Show details\nToolTip.Timestamp                            = \\u0421\\u0442\\u0432\\u043E\\u0440\\u044E\\u0432\\u0430\\u0442\\u0438 \\u0440\\u0435\\u0437\\u0435\\u0440\\u0432\\u043D\\u0443 \\u043A\\u043E\\u043F\\u0456\\u044E (\\u0456\\u043C'\\u044F \\u0444\\u0430\\u0439\\u043B\\u0443 \\u0431\\u0443\\u0434\\u0435 \\u043C\\u0456\\u0441\\u0442\\u0438\\u0442\\u0438 \\u0434\\u0430\\u0442\\u0443 \\u0442\\u0430 \\u0447\\u0430\\u0441)\nToolTip.ViewAttachment                       = View attachment\nToolTip.ZoomRegister                         = \\u0412\\u0456\\u0434\\u043A\\u0440\\u0438\\u0442\\u0438 \\u0440\\u0435\\u0454\\u0441\\u0442\\u0440 \\u0432 \\u043D\\u043E\\u0432\\u043E\\u043C\\u0443 \\u0432\\u0456\\u043A\\u043D\\u0456\n\nTransaction.AddShare        = \\u0414\\u043E\\u0434\\u0430\\u0442\\u0438 \\u0430\\u043A\\u0446\\u0456\\u0457 (\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438)\nTransaction.BuyShare        = \\u041A\\u0443\\u043F\\u0438\\u0442\\u0438 \\u0430\\u043A\\u0446\\u0456\\u0457\nTransaction.Dividend        = \\u0414\\u0438\\u0432\\u0456\\u0434\\u0435\\u043D\\u0434\\u0438\nTransaction.DoubleEntry     = \\u041F\\u043E\\u0434\\u0432\\u0456\\u0439\\u043D\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nTransaction.MergeShare      = \\u0417'\\u0454\\u0434\\u043D\\u0430\\u0442\\u0438 \\u0430\\u043A\\u0446\\u0456\\u0457\nTransaction.ReinvestDiv     = \\u0420\\u0435\\u0456\\u043D\\u0432\\u0435\\u0441\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0434\\u0438\\u0432\\u0456\\u0434\\u0435\\u043D\\u0434\\u0438\nTransaction.RemoveShare     = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438 \\u0430\\u043A\\u0446\\u0456\\u0457 (\\u043A\\u043E\\u0440\\u0435\\u043A\\u0442\\u0443\\u0432\\u0430\\u0442\\u0438)\nTransaction.ReturnOfCapital = \\u041F\\u043E\\u0432\\u0435\\u0440\\u043D\\u0435\\u043D\\u043D\\u044F \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B\\u0443\nTransaction.SellShare       = \\u041F\\u0440\\u043E\\u0434\\u0430\\u0442\\u0438 \\u0430\\u043A\\u0446\\u0456\\u0457\nTransaction.SingleEntry     = \\u041F\\u0440\\u043E\\u0441\\u0442\\u0438\\u0439 \\u0437\\u0430\\u043F\\u0438\\u0441 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439\nTransaction.Split           = \\u0421\\u043A\\u043B\\u0430\\u0434\\u0435\\u043D\\u0430 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u044F\nTransaction.SplitEntry      = \\u0427\\u0430\\u0441\\u0442\\u0438\\u043D\\u0430 \\u0441\\u043A\\u043B\\u0430\\u0434\\u0435\\u043D\\u043E\\u0457 \\u043E\\u043F\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\nTransaction.SplitShare      = \\u0420\\u043E\\u0437\\u0434\\u0456\\u043B\\u0438\\u0442\\u0438 \\u0430\\u043A\\u0446\\u0456\\u0457\nTransaction.TransferIn      = \\u0412\\u0445\\u0456\\u0434\\u043D\\u0438\\u0439 \\u043F\\u0435\\u0440\\u0435\\u043A\\u043B\\u0430\\u0434\nTransaction.TransferOut     = \\u0412\\u0438\\u0445\\u0456\\u0434\\u043D\\u0438\\u0439 \\u043F\\u0435\\u0440\\u0435\\u043A\\u043B\\u0430\\u0434\n\nWord.Add             = \\u0414\\u043E\\u0434\\u0430\\u0442\\u0438\nWord.All             = \\u0412\\u0441\\u0435\nWord.Balance         = \\u0411\\u0430\\u043B\\u0430\\u043D\\u0441\nWord.Buy             = \\u041A\\u0443\\u043F\\u0456\\u0432\\u043B\\u044F\nWord.Commodity       = \\u041F\\u0440\\u043E\\u0434\\u0443\\u043A\\u0442(\\u0438)\nWord.Copy            = Copy\nWord.Description     = \\u041E\\u043F\\u0438\\u0441\nWord.Difference      = \\u0420\\u0456\\u0437\\u043D\\u0438\\u0446\\u044F\nWord.Dividend        = \\u0414\\u0438\\u0432\\u0456\\u0434\\u0435\\u043D\\u0434\nWord.Exchange        = \\u041A\\u0443\\u0440\\u0441\nWord.Fees            = \\u0413\\u0440\\u043E\\u0448\\u043E\\u0432\\u0456 \\u0437\\u0431\\u043E\\u0440\\u0438\nWord.GrossExpense    = \\u0412\\u0441\\u044C\\u043E\\u0433\\u043E \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438\nWord.GrossIncome     = \\u0412\\u0441\\u044C\\u043E\\u0433\\u043E \\u043F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043E\\u043A\nWord.Interest        = \\u0412\\u0438\\u0433\\u043E\\u0434\\u0430\nWord.Into            = \\u0432\nWord.Invalid         = \\u041D\\u0435\\u0434\\u0456\\u0439\\u0441\\u043D\\u043E\nWord.Merge           = \\u043F\\u0456\\u0442\\u0438\nWord.Monthly         = \\u0429\\u043E\\u043C\\u0456\\u0441\\u044F\\u0447\\u043D\\u043E\nWord.Name            = \\u0406\\u043C'\\u044F\nWord.NetIncome       = \\u0427\\u0438\\u0441\\u0442\\u0438\\u0439 \\u043F\\u0440\\u0438\\u0431\\u0443\\u0442\\u043E\\u043A\nWord.NetWorth        = \\u0427\\u0438\\u0441\\u0442\\u0430 \\u0432\\u0430\\u0440\\u0442\\u0456\\u0441\\u0442\\u044C\nWord.NewBudget       = New Budget\nWord.None            = \\u041F\\u0443\\u0441\\u0442\\u043E\nWord.Quarterly       = \\u0429\\u043E\\u043A\\u0432\\u0430\\u0440\\u0442\\u0430\\u043B\\u044C\\u043D\\u043E\nWord.ReInvDiv        = \\u0420\\u0435\\u0456\\u043D\\u0432\\u0435\\u0441\\u0442\\u043E\\u0432\\u0430\\u043D\\u0438\\u0439 \\u0434\\u0438\\u0432\\u0456\\u0434\\u0435\\u043D\\u0434\nWord.Remove          = \\u0412\\u0438\\u0434\\u0430\\u043B\\u0438\\u0442\\u0438\nWord.ReturnOfCapital = \\u041F\\u043E\\u0432\\u0435\\u0440\\u043D\\u0435\\u043D\\u043D\\u044F \\u043A\\u0430\\u043F\\u0456\\u0442\\u0430\\u043B\\u0443\nWord.Seconds         = seconds\nWord.Security        = \\u0426\\u0456\\u043D\\u043D\\u0438\\u0439 \\u043F\\u0430\\u043F\\u0456\\u0440\nWord.Sell            = \\u041F\\u0440\\u043E\\u0434\\u0430\\u0436\nWord.Split           = Split\nWord.Subtotal        = \\u041F\\u0440\\u043E\\u043C\\u0456\\u0436\\u043D\\u0438\\u0439 \\u043F\\u0456\\u0434\\u0441\\u0443\\u043C\\u043E\\u043A\nWord.Total           = \\u0412\\u0441\\u044C\\u043E\\u0433\\u043E\nWord.Totals          = \\u041F\\u0456\\u0434\\u0441\\u0443\\u043C\\u043A\\u0438\nWord.Yearly          = \\u0429\\u043E\\u0440\\u043E\\u043A\\u0443\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_zh.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = \\u8D44\\u4EA7\nAccountType.Bank             = \\u94F6\\u884C\nAccountType.Cash             = \\u73B0\\u91D1\nAccountType.Checking         = \\u652F\\u7968\nAccountType.Credit           = \\u4FE1\\u7528\\u5361\nAccountType.Equity           = \\u8D44\\u4EA7\\u51C0\\u503C\nAccountType.Expense          = \\u652F\\u51FA\nAccountType.Income           = \\u6536\\u5165\nAccountType.Investment       = \\u6295\\u8D44\nAccountType.Liability        = \\u8D1F\\u503A\nAccountType.MoneyMarket      = \\u8D27\\u5E01\\u5E02\\u573A\\u5E10\\u6237\nAccountType.Mutual           = \\u5171\\u540C\\u57FA\\u91D1\nAccountType.Root             = \\u6839\nAccountType.SimpleInvestment = \\u7B80\\u5355\\u6295\\u8D44\n\nButton.AccTerms                = \\u4F7F\\u7528\\u4F1A\\u8BA1\\u540D\\u8BCD\nButton.Accounts                = \\u8D26\\u6237\nButton.AckSel                  = \\u786E\\u8BA4\\u6240\\u9009\nButton.Add                     = \\u6DFB\\u52A0\nButton.AllDates                = \\u6240\\u6709\\u65E5\\u671F\nButton.Amortize                = \\u5206\\u671F\\u6E05\\u507F\nButton.AnimationsEnabled       = \\u542F\\u7528\\u52A8\\u753B\\u6548\\u679C\nButton.AnyStatus               = \\u4EFB\\u4F55\\u72B6\\u6001\nButton.Apply                   = \\u5E94\\u7528\nButton.AssetAccounts           = \\u8D44\\u4EA7\\u5E10\\u6237\nButton.AutoReconcile           = \\u81EA\\u52A8\\u786E\\u5B9A\\u5206\\u62C6\\u4EA4\\u6613\nButton.AutoResizeColumns       = \\u81EA\\u52A8\\u8C03\\u6574\\u8868\\u683C\\u5217\\u5BBD\nButton.AvailSecurities         = \\u53EF\\u7528\\u7684\\u8BC1\\u5238\nButton.Back                    = \\u540E\\u9000\nButton.BankAccounts            = \\u94F6\\u884C\\u8D26\\u6237\nButton.BudgetMgr               = \\u9884\\u7B97\\u7BA1\\u7406\\u5668\nButton.Budgeting               = \\u9884\\u7B97\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = \\u53D6\\u6D88\nButton.CheckForUpdates         = \\u68C0\\u67E5\\u66F4\\u65B0\nButton.CheckReminders          = \\u68C0\\u67E5\\u63D0\\u9192\\u9879\\u76EE\nButton.Clear                   = \\u6E05\\u9664\nButton.ClearAll                = \\u5168\\u90E8\\u6E05\\u9664\nButton.Cleared                 = \\u5DF2\\u7ED3\\u6E05\nButton.Close                   = \\u5173\\u95ED\nButton.Compare                 = \\u6BD4\\u8F83\nButton.ConcatenateMemos        = \\u7EC4\\u5408\\u5907\\u5FD8\\u5F55\nButton.ConfirmReminderDelete   = \\u5220\\u9664\\u63D0\\u9192\\u65F6\\u8FDB\\u884C\\u786E\\u8BA4\nButton.ConfirmTransDelete      = \\u5220\\u9664\\u4EA4\\u6613\\u65F6\\u8FDB\\u884C\\u786E\\u8BA4\nButton.CopyToClip              = \\u590D\\u5236\\u5230\\u526A\\u8D34\\u677F\nButton.CreateTimeFile          = \\u9000\\u51FA\\u65F6\\u521B\\u5EFA\\u5E26\\u65F6\\u95F4\\u6233\\u7684\\u5907\\u4EFD\\u6587\\u4EF6\nButton.CreditAccounts          = \\u4FE1\\u7528\\u5E10\\u6237\nButton.Delete                  = \\u5220\\u9664\nButton.DeleteAll               = \\u5220\\u9664\\u5168\\u90E8\nButton.DeleteWeekends          = \\u5220\\u9664\\u5468\\u672B\nButton.DetailSplits            = \\u663E\\u793A\\u62C6\\u5206\\u7EC6\\u8282\nButton.Duplicate               = \\u5236\\u4F5C\\u526F\\u672C\nButton.Edit                    = \\u7F16\\u8F91\nButton.EnableAutoComplete      = \\u542F\\u7528\\u81EA\\u52A8\\u5B8C\\u6210\nButton.Enabled                 = \\u542F\\u7528\nButton.EndingBalance           = \\u671F\\u672B\\u4F59\\u989D\nButton.Enter                   = \\u5F55\\u5165\nButton.EnterDaysBefore         = \\u81EA\\u52A8\\u63D0\\u524D\\u82E5\\u5E72\\u5929\\u521B\\u5EFA\nButton.ExcludeFromBudget       = \\u4ECE\\u9884\\u7B97\\u4E2D\\u6392\\u9664\nButton.ExecuteNow              = \\u7ACB\\u5373\\u6267\\u884C\nButton.ExpenseAccounts         = \\u652F\\u51FA\\u8D26\\u6237\nButton.Export                  = \\u5BFC\\u51FA\\u4EA4\\u6613\nButton.ExportSpreadsheet       = \\u5BFC\\u51FA\\u7535\\u5B50\\u8868\\u683C\nButton.Filter                  = \\u7B5B\\u9009\nButton.Finish                  = \\u5B8C\\u6210\nButton.FinishLater             = \\u7A0D\\u540E\\u5B8C\\u6210\nButton.ForceDefaultCurrency    = \\u5F3A\\u5236\\u4F7F\\u7528\\u9ED8\\u8BA4\\u8D27\\u5E01\nButton.ForceGC                 = \\u5F3A\\u5236\\u4F18\\u5316\\u5185\\u5B58\nButton.HTTPAuth                = \\u4EE3\\u7406\\u9700\\u8981\\u6388\\u6743\nButton.Hidden                  = \\u9690\\u85CF\nButton.HideAccount             = \\u9690\\u85CF\\u8D26\\u6237\nButton.HideLockedAccount       = \\u9690\\u85CF\\u9501\\u5B9A\\u7684\\u8D26\\u6237\nButton.HidePlaceholderAccount  = \\u9690\\u85CF\\u5360\\u4F4D\\u7B26\\u5E10\\u6237\nButton.HideZeroBalance         = \\u9690\\u85CF\\u96F6\\u4F59\\u989D\\u5E10\\u6237\nButton.HistoricalFill          = \\u5386\\u53F2\\u586B\\u5145\nButton.Horizontal              = \\u6C34\\u5E73\nButton.IncludeSubAccounts      = \\u5305\\u62EC\\u5B50\\u8D26\\u6237\nButton.IncomeAccounts          = \\u6536\\u5165\\u8D26\\u6237\nButton.IncomeAndExpense        = \\u6536\\u5165\\u548C\\u652F\\u51FA\nButton.Insert                  = \\u63D2\\u5165\nButton.InvertBalances          = \\u53CD\\u8F6C\\u4F59\\u989D\nButton.InvertSelection         = \\u53CD\\u9009\nButton.Jump                    = \\u8DF3\\u8F6C\nButton.KeepFridays             = \\u4FDD\\u7559\\u661F\\u671F\\u4E94\nButton.KeepMondays             = \\u4FDD\\u7559\\u661F\\u671F\\u4E00\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u6700\\u8FD1120\\u5929\nButton.Last12Months            = \\u6700\\u8FD112\\u4E2A\\u6708\nButton.Last30Days              = \\u6700\\u8FD130\\u5929\nButton.Last60Days              = \\u6700\\u8FD160\\u5929\nButton.Last90Days              = \\u6700\\u8FD190\\u5929\nButton.LiabilityAccounts       = \\u8D1F\\u503A\\u8D26\\u6237\nButton.Locked                  = \\u9501\\u5B9A\nButton.MatchAccountOnly        = \\u4EC5\\u4F7F\\u7528\\u5E10\\u6237\\u7684\\u7279\\u5B9A\\u4EA4\\u6613\\u8FDB\\u884C\\u5339\\u914D\nButton.MatchAllTrans           = \\u4F7F\\u7528\\u6240\\u6709\\u4EA4\\u6613\\u5339\\u914D\nButton.MatchCaseSensitive      = \\u5339\\u914D\\u65F6\\u533A\\u5206\\u5927\\u5C0F\\u5199\nButton.Modify                  = \\u4FEE\\u6539\nButton.MonthlyBalance          = \\u6BCF\\u6708\\u4F59\\u989D\nButton.New                     = \\u65B0\\u589E\nButton.NewEmpty                = \\u65B0\\u589E\\u7A7A\\u767D\\u9884\\u7B97\nButton.NewHist                 = \\u65B0\\u589E\\u5386\\u53F2\\u9884\\u7B97\nButton.NewPayment              = \\u65B0\\u589E\\u4ED8\\u6B3E\nButton.Next                    = \\u4E0B\\u4E00\\u6B65\nButton.No                      = \\u5426\nButton.NoEndDate               = \\u65E0\\u7ED3\\u675F\\u65E5\\u671F\nButton.None                    = \\u6C92\\u6709\nButton.NotReconciled           = \\u672A\\u5BF9\\u5E10\nButton.Ok                      = \\u786E\\u5B9A\nButton.Open                    = \\u6253\\u5F00\nButton.OpenLastOnStartup       = \\u542F\\u52A8\\u65F6\\u6253\\u5F00\\u4E0A\\u6B21\\u7684\\u6587\\u4EF6\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = \\u9875\\u9762\\u8BBE\\u7F6E\nButton.PlaceHolder             = \\u5360\\u4F4D\\u7B26\\u8D26\\u6237\nButton.Portrait                = Portrait\nButton.Print                   = \\u6253\\u5370\nButton.PrintSample             = \\u6253\\u5370\\u6837\\u672C\nButton.Properties              = \\u5C5E\\u6027\nButton.Reconcile               = \\u5BF9\\u8D26\nButton.ReconcileBoth           = \\u6240\\u6709\\u4EA4\\u6613\\u8D26\\u6237\\u76F8\\u540C\\u7684\\u5BF9\\u8D26\\u72B6\\u6001\nButton.ReconcileDisable        = \\u7981\\u7528\\u81EA\\u52A8\\u5BF9\\u8D26\\u529F\\u80FD\nButton.ReconcileIncomeExpense  = \\u81EA\\u52A8\\u5BF9\\u6536\\u652F\\u8D26\\u6237\\u5BF9\\u8D26\nButton.Reconciled              = \\u5DF2\\u5BF9\\u8D26\nButton.Refresh                 = \\u5237\\u65B0\nButton.RegDate                 = \\u8BB0\\u4F4F\\u4E0A\\u6B21\\u4EA4\\u6613\\u65E5\\u671F\nButton.Register                = \\u767B\\u8BB0\nButton.RegisterFollowsList     = \\u6CE8\\u518C\\u8BB0\\u5F55\\u660E\\u7EC6\\u8868\nButton.RememberPassword        = \\u8BB0\\u4F4F\\u5BC6\\u7801\nButton.RemindLater             = \\u7A0D\\u540E\\u63D0\\u9192\nButton.Reminders               = \\u63D0\\u9192\nButton.RemoteServer            = \\u8FDC\\u7A0B\\u670D\\u52A1\\u5668\nButton.Remove                  = \\u5220\\u9664\nButton.RemoveOldBackups        = \\u5220\\u9664\\u65E7\\u7684\\u5907\\u4EFD\nButton.Rename                  = \\u66F4\\u540D\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = \\u5168\\u90E8\\u91CD\\u7F6E\nButton.Resize                  = \\u8C03\\u6574\\u5927\\u5C0F\nButton.RestoreDefault          = \\u6062\\u590D\\u4E3A\\u9ED8\\u8BA4\nButton.RestoreLastTranTab      = \\u6062\\u590D\\u4E0A\\u6B21\\u4F7F\\u7528\\u7684\\u4EA4\\u6613\\u5206\\u9875\nButton.RoundToWhole            = \\u5706\\u6574\nButton.RunningBalance          = \\u52A8\\u6001\\u5E73\\u8861\nButton.Save                    = \\u4FDD\\u5B58\nButton.SaveFilters             = \\u4FDD\\u5B58\\u8FC7\\u6EE4\\u5668\nButton.SaveImage               = \\u4FDD\\u5B58\\u56FE\\u50CF\nButton.Select                  = \\u9009\\u62E9\nButton.SelectAll               = \\u9009\\u62E9\\u5168\\u90E8\nButton.SelectText              = \\u83B7\\u5F97\\u7126\\u70B9\\u65F6\\u9009\\u4E2D\\u6587\\u672C\nButton.ShowCommodities         = \\u663E\\u793A\\u6240\\u6709\\u9879\\u76EE\nButton.ShowEmptyAccounts       = \\u663E\\u793A\\u96F6\\u4F59\\u989D\\u8D26\\u6237\nButton.ShowPercentValues       = \\u663E\\u793A\\u767E\\u5206\\u6BD4\nButton.ShowTimestamp           = \\u663E\\u793A\\u65F6\\u95F4\\u6233\nButton.Splits                  = \\u62C6\\u5206\nButton.Start                   = \\u5F00\\u59CB\nButton.Stop                    = \\u505C\\u6B62\nButton.SubstanceAnimations     = \\u5F00\\u542F\\u6F02\\u4EAE\\u754C\\u9762\\u52A8\\u753B\\uFF08Substance Look and Feel\\uFF09\nButton.SumColVis               = \\u663E\\u793A\\u603B\\u8BA1\\u5217\nButton.SumRowVis               = \\u663E\\u793A\\u6C47\\u603B\\u884C\nButton.ThemesEnabled           = \\u4F7F\\u7528\\u4E3B\\u9898\nButton.Timestamp               = \\u65F6\\u95F4\\u6233\nButton.Today                   = \\u4ECA\\u5929\nButton.UpdateCurrenciesStartup = \\u542F\\u52A8\\u65F6\\u66F4\\u65B0\\u8D27\\u5E01\\u6C47\\u7387\nButton.UpdateOnline            = \\u5728\\u7EBF\\u66F4\\u65B0\nButton.UpdateSecuritiesStartup = \\u542F\\u52A8\\u65F6\\u66F4\\u65B0\\u8BC1\\u5238\\u4FE1\\u606F\nButton.UseDailyRate            = \\u4F7F\\u7528\\u65E5\\u5468\\u671F\\u8D39\\u7387\nButton.UseEncryption           = \\u4F7F\\u7528\\u52A0\\u5BC6\nButton.UseFuzzyMatch           = \\u4F7F\\u7528\\u6A21\\u7CCA\\u5339\\u914D\nButton.UseLongNames            = \\u4F7F\\u7528\\u957F\\u540D\\u79F0\nButton.UseProxy                = \\u4F7F\\u7528\\u4EE3\\u7406\\u670D\\u52A1\\u5668\nButton.UseRegexForFilter       = \\u5BF9\\u8FC7\\u6EE4\\u5668\\u4F7F\\u7528\\u6B63\\u5219\\u8868\\u8FBE\\u5F0F\nButton.Vertical                = \\u7EB5\\u5411\nButton.Yes                     = \\u662F\nButton.Zoom                    = \\u7F29\\u653E\n\nColumn.Account                        = \\u8D26\\u6237\nColumn.AccountName                    = \\u8D26\\u6237\\u540D\nColumn.Action                         = \\u64CD\\u4F5C\nColumn.Actual                         = \\u5B9E\\u9645\\u503C\nColumn.Amount                         = \\u91D1\\u989D\nColumn.Approve                        = \\u6838\\u51C6\nColumn.Balance                        = \\u4F59\\u989D\nColumn.Budgeted                       = \\u9884\\u7B97\\u503C\nColumn.Charge                         = \\u624B\\u7EED\\u8D39\nColumn.Close                          = \\u6536\\u76D8\\u4EF7\nColumn.Clr                            = \\u5BF9\\u8D26\\u8C03\\u8282\nColumn.Code                           = \\u79D1\\u76EE\\u4EE3\\u7801\nColumn.Commodity                      = \\u5546\\u54C1\nColumn.CostBasis                      = \\u6210\\u672C\\u57FA\\u7840\nColumn.Credit                         = \\u8D37\\u65B9\nColumn.Currency                       = \\u8D27\\u5E01\nColumn.Date                           = \\u65E5\\u671F\nColumn.Day                            = \\u5929\nColumn.Debit                          = \\u501F\\u65B9\nColumn.Decrease                       = \\u51CF\\u5C11\nColumn.Deposit                        = \\u5B58\\u6B3E\nColumn.Description                    = \\u8BF4\\u660E\nColumn.Due                            = \\u622A\\u6B62\nColumn.Enabled                        = \\u542F\\u7528\nColumn.Entries                        = \\u6761\\u76EE\nColumn.Event                          = \\u4E8B\\u4EF6\nColumn.ExchangeRate                   = \\u6C47\\u7387\nColumn.Expense                        = \\u652F\\u51FA\nColumn.Freq                           = \\u9891\\u7387\nColumn.Gain                           = \\u5229\\u6DA6\nColumn.High                           = \\u6700\\u9AD8\\u4EF7\nColumn.Income                         = \\u6536\\u5165\nColumn.Increase                       = \\u589E\\u52A0\nColumn.Investment                     = \\u6295\\u8D44\nColumn.LastPosted                     = \\u4E0A\\u6B21\\u63D0\\u9192\nColumn.Loss                           = \\u635F\\u5931\nColumn.Low                            = \\u6700\\u4F4E\\u4EF7\nColumn.Memo                           = \\u5907\\u6CE8\nColumn.MktValue                       = \\u5E02\\u4EF7\nColumn.Month                          = \\u6708\nColumn.Num                            = \\u5E8F\\u53F7\nColumn.Payee                          = \\u6536\\u6B3E\\u4EBA\nColumn.Payment                        = \\u4ED8\\u6B3E\nColumn.Percentile                     = Percentile\nColumn.Period                         = \\u5468\\u671F\nColumn.Price                          = \\u5F53\\u524D\\u4EF7\\u683C\nColumn.Print                          = \\u6253\\u5370\nColumn.PropName                       = \\u5C5E\\u6027\\u540D\\u79F0\nColumn.PropVal                        = \\u5C5E\\u6027\\u503C\nColumn.Quantity                       = \\u6570\\u91CF\nColumn.Rebate                         = \\u6298\\u6263\nColumn.Receive                        = \\u6536\\u6B3E\nColumn.ReconciledBalance              = \\u5DF2\\u5BF9\\u8D26\\u4F59\\u989D\nColumn.Remaining                      = \\u5269\\u4F59\\u503C\nColumn.Script                         = Script\nColumn.Security                       = \\u8BC1\\u5238\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = \\u6D88\\u8D39\nColumn.Timestamp                      = \\u65F6\\u95F4\\u6233\nColumn.Total                          = \\u5408\\u8BA1\nColumn.TotalCostBasis                 = \\u652F\\u51FA\\u57FA\\u7840\\u603B\\u8BA1\nColumn.Type                           = \\u7C7B\\u578B\nColumn.Value                          = \\u4EF7\\u503C\nColumn.Volume                         = \\u4EA4\\u6613\\u91CF\nColumn.Withdrawal                     = \\u63D0\\u6B3E\n\nDataStoreType.Bxds = \\u4E8C\\u8FDB\\u5236\\u6587\\u4EF6\nDataStoreType.H2   = H2\\u5173\\u7CFB\\u578B\\u6570\\u636E\\u5E93\nDataStoreType.HSQL = HyperSQL\\u5173\\u7CFB\\u578B\\u6570\\u636E\\u5E93\nDataStoreType.XML  = XML\\u6587\\u4EF6\n\nItem.Amount         = \\u6570\\u91CF\nItem.CashDeposit    = \\u73B0\\u91D1\\u5B58\\u6B3E\nItem.CashWithdrawal = \\u73B0\\u91D1\\u63D0\\u6B3E\nItem.Date           = \\u65E5\\u671F\nItem.EFT            = \\u7535\\u5B50\\u4EA4\\u6613/EFT\nItem.Memo           = \\u5907\\u6CE8\nItem.NextNum        = \\u4E0B\\u4E00\\u5E8F\\u53F7\nItem.Payee          = \\u6536\\u6B3E\\u4EBA\nItem.Trans          = \\u8F6C\\u8D26\n\nLabel.AccentColor         = \\u5F3A\\u8C03\\u8272\\uFF1A\nLabel.Account             = \\u8D26\\u6237:\nLabel.AccountCode         = \\u79D1\\u76EE\\u4EE3\\u7801:\nLabel.AccountNumber       = \\u8D26\\u53F7\\uFF1A\nLabel.AccountOptions      = \\u8D26\\u6237\\u9009\\u9879:\nLabel.AccountSeparator    = \\u8D26\\u6237\\u5206\\u9694\\u7B26\\u53F7:\nLabel.AccountStatus       = \\u8D26\\u6237\\u72B6\\u6001:\nLabel.AccountType         = \\u8D26\\u6237\\u7C7B\\u578B:\nLabel.Action              = \\u64CD\\u4F5C:\nLabel.Amount              = \\u91D1\\u989D:\nLabel.AnIntRate           = \\u5E74\\u5229\\u7387:\nLabel.Available           = \\u53EF\\u7528\\u7684:\nLabel.Balance             = \\u4F59\\u989D:\nLabel.BankAccount         = \\u94F6\\u884C\\u8D26\\u53F7:\nLabel.BankID              = \\u94F6\\u884C\\u7F16\\u53F7:\nLabel.BaseAccount         = \\u57FA\\u672C\\u8D26\\u6237:\nLabel.BaseColor           = \\u57FA\\u672C\\u8272\\uFF1A\nLabel.Bottom              = Bottom:\nLabel.By                  = \\u4F9D\\u636E:\nLabel.CashBalance         = \\u73B0\\u91D1\\u4F59\\u989D:\nLabel.Close               = \\u6536\\u76D8\\u4EF7:\nLabel.Color=Color:\nLabel.Commodity           = \\u5546\\u54C1:\nLabel.CompDaysPerYear     = \\u65E5\\u590D\\u5229\\u7387:\nLabel.CompPerTerm         = \\u671F\\u95F4\\u590D\\u5229\\u7387:\nLabel.Compare             = \\u6BD4\\u8F83:\nLabel.ConfirmPassword     = \\u786E\\u8BA4\\u5BC6\\u7801:\nLabel.ConnTimeout         = \\u8FDE\\u63A5\\u8D85\\u65F6\\u65F6\\u957F:\nLabel.Count               = \\u8BA1\\u6570:\nLabel.CreateCurr          = \\u81EA\\u5B9A\\u5E01\\u79CD:\nLabel.CssFiles            = CSS\\u6587\\u4EF6\nLabel.CsvFiles            = Csv\\u6587\\u4EF6\nLabel.Curr/Comm           = \\u5E01\\u79CD / \\u5546\\u54C1:\nLabel.Currencies          = \\u5E01\\u79CD:\nLabel.Currency            = \\u5E01\\u79CD:\nLabel.Current             = \\u5F53\\u524D:\nLabel.DatabaseBackend     = \\u6570\\u636E\\u5E93\\u540E\\u53F0:\nLabel.DatabaseName        = \\u6570\\u636E\\u5E93\\u4F4D\\u7F6E:\nLabel.DatabaseServer      = \\u6570\\u636E\\u5E93\\u670D\\u52A1\\u5668:\nLabel.Date                = \\u65E5\\u671F:\nLabel.DateFormat          = \\u65E5\\u671F\\u683C\\u5F0F:\nLabel.DaysPastDue         = \\u903E\\u671F\\u5929\\u6570:\nLabel.DefaultCurrency     = \\u9ED8\\u8BA4\\u5E01\\u79CD:\nLabel.DelaySec            = \\u5EF6\\u65F6\\uFF08\\u79D2\\uFF09\nLabel.Description         = \\u63CF\\u8FF0:\nLabel.DestAccount         = \\u76EE\\u6807\\u8D26\\u6237:\nLabel.Difference          = \\u5DEE\\u5F02:\nLabel.Dividend            = \\u5206\\u7EA2:\nLabel.EndDate             = \\u622A\\u6B62\\u65E5\\u671F:\nLabel.EndOn               = \\u622A\\u6B62\\u5728:\nLabel.EndRow              = \\u672B\\u884C:\nLabel.EndingBalance       = \\u6700\\u7EC8\\u4F59\\u989D:\nLabel.EquityAccount       = \\u8D26\\u6237\\u8D44\\u4EA7\\u51C0\\u503C:\nLabel.EscrowPmi           = \\u4FE1\\u6258\\u3001PMI\\u7B49:\nLabel.Event               = \\u4E8B\\u4EF6:\nLabel.Every               = \\u6BCF\nLabel.ExchangeAmount      = \\u5151\\u6362\\u91D1\\u989D:\nLabel.ExchangeRate        = \\u6C47\\u7387:\nLabel.ExchangedAmount     = \\u5151\\u6362\\u6240\\u5F97\\u91D1\\u989D:\nLabel.Fees                = \\u8D39\\u7528:\nLabel.FeesAccount         = \\u8D39\\u7528\\u652F\\u51FA\\u8D26\\u6237:\nLabel.FileName            = \\u6587\\u4EF6\\u540D:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = \\u9996\\u6B21\\u4ED8\\u6B3E\\u65E5\\u671F:\nLabel.FocusColor          = \\u805A\\u7126\\u984F\\u8272\\uFF1A\nLabel.Format              = Format:\nLabel.Frequency           = \\u9891\\u7387:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = \\u83B7\\u5229:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = \\u9AD8\\u5EA6:\nLabel.High                = \\u6700\\u9AD8\\u4EF7:\nLabel.Host                = \\u4E3B\\u673A:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = \\u6536\\u5165\\u8D26\\u6237:\nLabel.InterestAccount     = \\u5229\\u606F\\u8D26\\u6237:\nLabel.LastOccurrence      = \\u4E0A\\u6B21\\u53D1\\u751F:\nLabel.Layout              = \\u7248\\u9762:\nLabel.Left                = Left:\nLabel.LoanTerm            = \\u8D37\\u6B3E\\u65F6\\u95F4:\nLabel.Low                 = \\u6700\\u4F4E\\u4EF7:\nLabel.MarketValue         = \\u5E02\\u573A\\u4EF7\\u683C:\nLabel.MaxBackupCount      = \\u4FDD\\u7559\\u5907\\u4EFD\\u7684\\u6700\\u5927\\u6570\\u91CF:\nLabel.Memo                = \\u5907\\u6CE8:\nLabel.Monospace           = \\u7B49\\u5BBD\\u5B57\\u4F53:\nLabel.Name                = \\u540D\\u79F0:\nLabel.NetIncome           = \\u51C0\\u6536\\u5165:\nLabel.NewPassword         = \\u65B0\\u5BC6\\u7801:\nLabel.NextPayDate         = \\u4E0B\\u6B21\\u4ED8\\u6B3E\\u65E5\\u671F:\nLabel.Notes               = \\u6CE8\\u91CA:\nLabel.NumTrans            = \\u4EA4\\u6613\\u6B21\\u6570:\nLabel.Number              = \\u5E8F\\u53F7:\nLabel.OfxFiles            = Ofx\\u6587\\u4EF6\nLabel.OpenStateDate       = \\u521D\\u59CB\\u8D22\\u52A1\\u65E5\\u671F:\nLabel.OpeningBalance      = \\u521D\\u59CB\\u4F59\\u989D:\nLabel.OrigLoanAmt         = \\u521D\\u59CB\\u8D37\\u6B3E\\u91D1\\u989D:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = \\u5BC6\\u7801:\nLabel.Path                = \\u8DEF\\u5F84:\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = \\u6BCF\\u5E74\\u4ED8\\u6B3E\\u989D:\nLabel.Payee               = \\u6536\\u6B3E\\u4EBA:\nLabel.Period              = \\u5468\\u671F\\uFF1A\nLabel.Port                = \\u7AEF\\u53E3:\nLabel.Prefix              = \\u524D\\u7F00:\nLabel.Price               = \\u4EF7\\u683C:\nLabel.Proportional        = \\u6BD4\\u4F8B\\u5B57\\u4F53:\nLabel.Quantity            = \\u6570\\u91CF:\nLabel.QuoteSource         = \\u62A5\\u4EF7\\u6765\\u6E90:\nLabel.ReceivingAccount    = \\u5E94\\u6536\\u6B3E\\u8D26\\u6237:\nLabel.ReconciledBalance   = \\u5DF2\\u5BF9\\u8D26\\u4F59\\u989D:\nLabel.RemindLater         = \\u7A0D\\u540E\\u63D0\\u9192:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = \\u91CD\\u590D:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = \\u4EA4\\u6613\\u5E01\\u79CD:\nLabel.Resolution          = \\u7C92\\u5EA6:\nLabel.ReturnOfCapital     = \\u8D44\\u672C\\u56DE\\u62A5:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = \\u6700\\u5C0F\\u4EA4\\u6613\\u5355\\u4F4D:\nLabel.Securities          = \\u8BC1\\u5238:\nLabel.Security            = \\u8BC1\\u5238:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = \\u663E\\u793A\\u7A7A\\u8D26\\u6237\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = xls\\u6587\\u4EF6\nLabel.StartDate           = \\u5F00\\u59CB\\u65E5\\u671F:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = \\u5F00\\u59CB\\u4F4D\\u7F6E:\nLabel.StartRow            = \\u8D77\\u59CB\\u884C\nLabel.StatementDate       = \\u8D22\\u52A1\\u62A5\\u544A\\u65E5\\u671F\\uFF1A\nLabel.StorageType         = \\u5B58\\u50A8\\u5F62\\u5F0F:\nLabel.Suffix              = \\u540E\\u7F00:\nLabel.Symbol              = \\u7B26\\u53F7:\nLabel.TargetBalance       = \\u76EE\\u6807\\u4F59\\u989D:\nLabel.Top                 = Top:\nLabel.Total               = \\u5408\\u8BA1:\nLabel.Transaction         = \\u4EA4\\u6613:\nLabel.TransferFrom        = \\u8F6C\\u51FA\\u81EA:\nLabel.TransferTo          = \\u8F6C\\u8D26\\u5230:\nLabel.Type                = \\u7C7B\\u578B:\nLabel.Units               = Units:\nLabel.UserName            = \\u7528\\u6237\\u540D:\nLabel.Value               = \\u4EF7\\u503C:\nLabel.Verify              = \\u6821\\u9A8C:\nLabel.VerifyPassword      = \\u786E\\u8BA4\\u5BC6\\u7801:\nLabel.Visible             = \\u53EF\\u89C1\\u7684:\nLabel.Volume              = \\u6210\\u4EA4\\u91CF:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML\\u6587\\u4EF6\nLabel.Year                = \\u5E74\nLabel.jGnashFiles         = jGnash\\u652F\\u6301\\u7684\\u6587\\u4EF6\n\nMenu.About.Name                       = \\u5173\\u4E8E...\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = \\u8D26\\u6237\nMenu.AccountRegister.Name             = \\u8D26\\u6237\\u767B\\u8BB0...\nMenu.AddRemoveCurrency.Name           = \\u6DFB\\u52A0/\\u5220\\u9664\\u5E01\\u79CD...\nMenu.BackgroundCurrencyUpdate.Name    = \\u66F4\\u65B0\\u8D27\\u5E01\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = \\u66F4\\u65B0\\u8BC1\\u5238\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = \\u4F59\\u989D\\u8868...\nMenu.BaseColor.Name                   = \\u66F4\\u6539\\u57FA\\u672C\\u989C\\u8272\\u2026\nMenu.BudgetManager.Name               = \\u9884\\u7B97\\u7BA1\\u7406\\u5668\nMenu.ChangeCredentials.Name           = \\u4FEE\\u6539\\u6570\\u636E\\u5E93\\u5BC6\\u7801...\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = \\u56FE\\u8868\nMenu.Cleared.Name                     = \\u5DF2\\u7ED3\\u6E05\nMenu.Close.Name                       = \\u5173\\u95ED\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = \\u5173\\u95ED\\u6240\\u6709\\u7A97\\u53E3\nMenu.ConfigImportFilters.Name         = \\u914D\\u7F6E\\u4EA4\\u6613\\u5BFC\\u5165\\u8FC7\\u6EE4\\u5668...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = \\u590D\\u5236\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = \\u8D27\\u5E01\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = \\u9009\\u62E9\\u9ED8\\u8BA4\\u8D27\\u5E01...\nMenu.DefaultCurrency.Tooltip          = \\u66F4\\u6539\\u9ED8\\u8BA4\\u8D27\\u5E01...\nMenu.Delete.Name                      = \\u5220\\u9664\nMenu.Duplicate.Name                   = \\u5236\\u4F5C\\u526F\\u672C\nMenu.Edit.Name                        = \\u7F16\\u8F91\nMenu.EditTranNumList.Name             = \\u7F16\\u8F91\\u4EA4\\u6613\\u53F7\\u7801\\u5217\\u8868...\nMenu.Exit.Name                        = \\u9000\\u51FA\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = \\u5BFC\\u51FA\nMenu.ExportAccounts.Name              = \\u5BFC\\u51FA\\u8D26\\u6237...\nMenu.File.Archive                     = \\u5F52\\u6863...\nMenu.File.Name                        = \\u6587\\u4EF6\nMenu.Filter.Name                      = \\u7B5B\\u9009\\u8FC7\\u6EE4\\u5668\nMenu.FontSize.Name                    = \\u66F4\\u6539\\u9ED8\\u8BA4\\u5B57\\u4F53\\u5B57\\u53F7...\nMenu.Help.Name                        = \\u5E2E\\u52A9\nMenu.Hide.Name                        = \\u9690\\u85CF\nMenu.HistoryChart.Name                = \\u5386\\u53F2\\u6570\\u636E\\u56FE\\u8868\nMenu.HistoryCommodity.Name            = \\u5386\\u53F2\nMenu.HistoryImport.Name               = \\u5BFC\\u5165\\u5386\\u53F2\\u6570\\u636E\nMenu.IEBarChart.Name                  = \\u6536\\u5165/\\u652F\\u51FA\\u67F1\\u72B6\\u56FE\nMenu.IEPieChart.Name                  = \\u6536\\u5165/\\u652F\\u51FA\\u997C\\u56FE\nMenu.Import.Name                      = \\u5BFC\\u5165\nMenu.ImportAccounts.Name              = \\u5BFC\\u5165\\u8D26\\u6237...\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = \\u5BFC\\u5165jGnash...\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = QIF...\nMenu.Jump.Name                        = \\u8DF3\\u8F6C\nMenu.ListOfAccounts.Name              = \\u8D26\\u6237\\u5217\\u8868\\u2026\nMenu.Locale.Name                      = \\u66F4\\u6539\\u8BED\\u8A00...\nMenu.LookAndFeel.Name                 = \\u5916\\u89C2\nMenu.MarkAs.Name                      = \\u6807\\u8BB0\\u4E3A\nMenu.Modify.Name                      = \\u4FEE\\u6539\nMenu.ModifyCommodity.Name             = \\u521B\\u5EFA/\\u4FEE\\u6539\\u5546\\u54C1...\nMenu.ModifyCurrency.Name              = \\u4FEE\\u6539\\u5E01\\u79CD...\nMenu.ModifyExchangeRates.Name         = \\u7F16\\u8F91\\u6C47\\u7387...\nMenu.MonthBalance.Name                = \\u6708\\u4F59\\u989D...\nMenu.MonthBalanceCompare.Name         = \\u6708\\u4F59\\u989D\\u6BD4\\u5BF9\nMenu.MonthEndBalance.Name             = \\u6708\\u7EC8\\u4F59\\u989D...\nMenu.MonthEndBalanceCSV.Name          = \\u6708\\u7EC8\\u4F59\\u989D (CSV)...\nMenu.NetWorth.Name                    = \\u8D44\\u672C\\u51C0\\u503C...\nMenu.New.Name                         = \\u65B0\\u589E\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = \\u521B\\u5EFA\\u65B0\\u63D0\\u9192\nMenu.Open.Name                        = \\u6253\\u5F00\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = \\u9009\\u9879\nMenu.Option.Tooltip                   = \\u66F4\\u6539\\u7A0B\\u5E8F\\u9009\\u9879\nMenu.PackDatabase.Name                = \\u6253\\u5305\\u6570\\u636E\\u5E93...\nMenu.PayeePieChart.Name               = \\u6309\\u6536\\u6B3E\\u4EBA\\u5206\\u7C7B\\u7684\\u6536\\u5165/\\u652F\\u51FA\\u997C\\u56FE\nMenu.PeriodicAccountBalance.Name      = \\u5B9A\\u671F\\u5E10\\u6237\\u4F59\\u989D...\nMenu.Portfolio.Name                   = \\u6295\\u8D44\\u7EC4\\u5408...\nMenu.ProfitLoss.Name                  = \\u635F\\u76CA\\u8868...\nMenu.ProfitLossTXT.Name               = \\u635F\\u76CA\\u8868 (Text)...\nMenu.Reconcile.Name                   = \\u5BF9\\u8D26\nMenu.Reconciled.Name                  = \\u5DF2\\u5BF9\\u8D26\nMenu.RecurringList.Name               = \\u5B9A\\u671F\\u91CD\\u590D\\u6267\\u884C\\u7684\\u4E8B\\u52A1...\nMenu.Register.Name                    = \\u767B\\u8BB0...\nMenu.Reports.Name                     = \\u62A5\\u8868\nMenu.RunJavaScript.Name               = Run JavaScript...\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = \\u4FDD\\u5B58\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = \\u53E6\\u5B58\\u4E3A...\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = \\u8BC1\\u5238\nMenu.SetPassword.Name                 = \\u8BBE\\u7F6E\\u5BC6\\u7801...\nMenu.Show.Name                        = \\u663E\\u793A\nMenu.ShutdownServer.Name              = \\u5173\\u95ED\\u670D\\u52A1\\u5668\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = \\u663E\\u793A\\u4E3B\\u9898\nMenu.Tools.Name                       = \\u5DE5\\u5177\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = \\u672A\\u5BF9\\u8D26\nMenu.View.Name                        = \\u89C6\\u56FE\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = \\u60A8\\u5DF2\\u7ECF\\u9605\\u8BFB\\u3001\\u7406\\u89E3\\u5E76\\u63A5\\u53D7\\u8FD9\\u4E9B\\u8BB8\\u53EF\\u6761\\u6B3E\nMessage.AccountAdd                   = \\u8D26\\u53F7\\u5DF2\\u6DFB\\u52A0\nMessage.AccountCode                  = \\u8D26\\u6237\\u4EE3\\u7801\\u5FC5\\u987B\\u552F\\u4E00\\uFF1A\\u539F\\u4EE3\\u7801\\u672A\\u4FEE\\u6539\nMessage.AccountLocked                = \\u8D26\\u6237\\u5DF2\\u7ECF\\u88AB\\u9501\\u5B9A\nMessage.AccountModify                = \\u8D26\\u6237\\u5DF2\\u4FEE\\u6539\nMessage.AccountMoveFailed            = \\u4E0D\\u80FD\\u79FB\\u52A8\\u8BE5\\u8D26\\u6237\nMessage.AccountRemove                = \\u8D26\\u6237\\u5DF2\\u5220\\u9664\nMessage.AntiAlias                    = \\u5F00\\u542F\\u201C\\u6587\\u672C\\u53CD\\u952F\\u9F7F\\u201D\\uFF01\nMessage.AutoSaveOff                  = \\u81EA\\u52A8\\u5B58\\u6863\\u5DF2\\u7ECF\\u5173\\u95ED\nMessage.AutoSaveOn                   = \\u81EA\\u52A8\\u5B58\\u6863\\u5DF2\\u7ECF\\u5F00\\u542F\nMessage.CSVFile                      = \\u9017\\u53F7\\u5206\\u9694\\u6587\\u4EF6(*.csv)\nMessage.CheckRecurring               = \\u6B63\\u5728\\u68C0\\u67E5\\u65B0\\u7684\\u5FAA\\u73AF\\u4E8B\\u4EF6\nMessage.ClosingFile                  = \\u6B63\\u5728\\u5173\\u95ED\\u6587\\u4EF6\nMessage.CollectingReportData         = \\u6B63\\u5728\\u6536\\u96C6\\u62A5\\u544A\\u6570\\u636E\nMessage.CompilingReport              = \\u6B63\\u5728\\u751F\\u6210\\u62A5\\u544A\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = \\u786E\\u8BA4\\u5220\\u9664\\u9009\\u4E2D\\u7684\\u6B64\\u6761\\u9884\\u7B97?\nMessage.ConfirmMultipleBudgetDelete  = \\u786E\\u8BA4\\u5220\\u9664\\u9009\\u4E2D\\u7684\\u591A\\u6761\\u9884\\u7B97?\nMessage.ConfirmMultipleTransDelete   = \\u786E\\u8BA4\\u5220\\u9664\\u9009\\u5B9A\\u7684\\u591A\\u6761\\u4EA4\\u6613\\u8BB0\\u5F55?\nMessage.ConfirmReminderDelete        = \\u5220\\u9664\\u9009\\u4E2D\\u7684\\u63D0\\u9192?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = \\u5220\\u9664\\u9009\\u4E2D\\u7684\\u4EA4\\u6613?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = \\u9ED8\\u8BA4\\u5E01\\u522B\\u66F4\\u6539\\u4E3A\nMessage.DownloadingX                 = \\u6B63\\u5728\\u4E0B\\u8F7D {0}\nMessage.EngineStart                  = \\u542F\\u52A8\\u5F15\\u64CE\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = \\u65E0\\u6CD5\\u65B0\\u589E\\u8D26\\u6237\nMessage.Error.AccountRemove          = \\u65E0\\u6CD5\\u79FB\\u9664\\u8D26\\u6237\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = \\u65E0\\u6CD5\\u65B0\\u589E\\u5546\\u54C1\nMessage.Error.AddCurrency            = \\u65E0\\u6CD5\\u65B0\\u589E\\u8D27\\u5E01\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = \\u5148\\u65B0\\u589E\\u57FA\\u672C\\u8D26\\u6237\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = \\u6CA1\\u6709\\u627E\\u5230\\u6587\\u4EF6\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = The license was not accepted\nMessage.Error.LoadingFile            = Error loading file\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = \\u65E0\\u6CD5\\u4FEE\\u6539\\u5546\\u54C1\nMessage.Error.ModifyCurrency         = \\u65E0\\u6CD5\\u4FEE\\u6539\\u8D27\\u5E01\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = \\u6587\\u4EF6\\u5DF2\\u5173\\u95ED\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = \\u6587\\u4EF6\\u8F7D\\u5165\\u5B8C\\u6210\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash \\u9700\\u8981 Java 11 \\u6216\\u66F4\\u9AD8\\u7248\\u672C\nMessage.LoadReportFail               = \\u65E0\\u6CD5\\u8F7D\\u5165\\u62A5\\u544A\nMessage.LoadingFile                  = \\u6B63\\u5728\\u8F7D\\u5165\\u6587\\u4EF6\nMessage.LocaleChange                 = \\u9ED8\\u8BA4\\u8BED\\u8A00\\u66F4\\u6539\\u4E3A:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = \\u4E0D\\u91CD\\u590D\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = \\u65E0\\u6CD5\\u89E3\\u6790\\u62A5\\u8868\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = \\u51C6\\u5907\\u5173\\u95ED\\u7A0B\\u5E8F\nMessage.ProcessingReportData         = \\u6B63\\u5728\\u5904\\u7406\\u62A5\\u8868\\u6570\\u636E\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = \\u6210\\u529F\\u8F7D\\u5165\\u62A5\\u8868\\u6A21\\u7EC4\nMessage.ReportWait                   = \\u6B63\\u5728\\u8F7D\\u5165\\u62A5\\u8868\\u6A21\\u7EC4\\u4E2D \\u8BF7\\u7A0D\\u540E\nMessage.RestartLocale                = \\u8BF7\\u91CD\\u542F\\u7A0B\\u5E8F\\u4EE5\\u4F7F\\u65B0\\u8BBE\\u5B9A\\u7684\\u8BED\\u8A00\\u914D\\u7F6E\\u751F\\u6548\nMessage.SavingFile                   = \\u6B63\\u5728\\u4FDD\\u5B58\\u6587\\u4EF6\nMessage.SearchWait                   = \\u6B63\\u5728\\u67E5\\u627E, \\u8BF7\\u7A0D\\u540E\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = \\u8F93\\u5165\\u5F00\\u59CB\\u53CA\\u7ED3\\u675F\\u65E5\\u671F\nMessage.StoreBackup                  = \\u4FDD\\u5B58\\u5907\\u4EFD\\u81F3:\nMessage.StoreComplete                = \\u6587\\u4EF6\\u5B58\\u50A8\\u5B8C\\u6BD5\nMessage.StoreWait                    = \\u6B63\\u5728\\u7B49\\u5F85\\u6587\\u4EF6\\u5B58\\u50A8\\u5B8C\\u6BD5\nMessage.TXTFile                      = \\u6587\\u672C\\u6587\\u4EF6 (*.txt)\nMessage.TransactionAccountLocked     = \\u6DFB\\u52A0\\u4EA4\\u6613\\u5931\\u8D25\\uFF0C\\u4EA4\\u6613\\u8D26\\u6237\\u5DF2\\u9501\\u5B9A\nMessage.TransactionAdd               = \\u6DFB\\u52A0\\u4EA4\\u6613\\u5B8C\\u6210\nMessage.TransactionModifyLocked      = \\u4FEE\\u6539\\u4EA4\\u6613\\u5931\\u8D25\\uFF0C\\u4EA4\\u6613\\u8D26\\u6237\\u5DF2\\u9501\\u5B9A\nMessage.TransactionRemove            = \\u4EA4\\u6613\\u5DF2\\u5220\\u9664\nMessage.TransactionRemoveLocked      = \\u5220\\u9664\\u4EA4\\u6613\\u5931\\u8D25\\uFF0C\\u4EA4\\u6613\\u8D26\\u6237\\u5DF2\\u9501\\u5B9A\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = \\u6B63\\u5728\\u4F7F\\u7528\\u8BE5\\u4EA4\\u6613\\u5546\\u54C1\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = \\u6B63\\u5728\\u4F7F\\u7528\\u8BE5\\u5E01\\u79CD\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = \\u94F6\\u884C\\u8D26\\u6237\nName.ExpenseAccounts = \\u652F\\u51FA\\u8D26\\u6237\nName.IncomeAccounts  = \\u6536\\u5165\\u8D26\\u6237\nName.Root            = \\u6839\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = \\u4ECE {0,date,long} \\u81F3 {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = \\u7B2C {0} \\u9875/\\u5171 {1} \\u9875\nPattern.QuarterOfYear  = \\u7B2C {0,number,#} \\u5B63\\u5EA6/\\u5171 {1,number,#} \\u5B63\\u5EA6\nPattern.WeekOfYear     = \\u7B2C {0,number,00} \\u5468/\\u5171 {1,number,#} \\u5468\n\nPeriod.10Min     = 10 \\u5206\\u949F\nPeriod.15Min     = 15 \\u5206\\u949F\nPeriod.1Day      = 1 \\u5929\nPeriod.1Hr       = 1 \\u5C0F\\u65F6\nPeriod.2Hr       = 2 \\u5C0F\\u65F6\nPeriod.30Min     = 30 \\u5206\\u949F\nPeriod.5Min      = 5 \\u5206\\u949F\nPeriod.8Hr       = 8 \\u5C0F\\u65F6\nPeriod.BiWeekly  = \\u53CC\\u5468\nPeriod.Daily     = \\u6BCF\\u65E5\nPeriod.Monthly   = \\u6BCF\\u6708\nPeriod.NextStart = \\u4E0B\\u6B21\\u542F\\u52A8 jGnash \\u65F6\nPeriod.None      = \\u65E0\nPeriod.OnlyOnce  = \\u4EC5\\u4E00\\u6B21\nPeriod.Quarterly = \\u6BCF\\u5B63\\u5EA6\nPeriod.Weekly    = \\u6BCF\\u5468\nPeriod.Yearly    = \\u6BCF\\u5E74\n\nQuestion.DeleteAttachment = \\u6B64\\u9879\\u4EA4\\u6613\\u5305\\u542B\\u4E00\\u4E2A\\u9644\\u4EF6.\\n\\n\\u662F\\u5426\\u540C\\u65F6\\u5220\\u9664\\u8BE5\\u9644\\u4EF6?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = \\u80A1\\u7968\\u5206\\u7EA2\nSecurityEvent.Price    = \\u80A1\\u7968\\u5B9A\\u4EF7\nSecurityEvent.Split    = \\u80A1\\u7968\\u62C6\\u5206\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = \\u6309\\u8D26\\u6237\\u4F59\\u989D\nSortOrder.AccountName        = \\u6309\\u8D26\\u6237\\u540D\\u79F0\n\nState.Cleared       = C\nState.NotReconciled = N\nState.Reconciled    = R\n\nTab.About           = \\u5173\\u4E8E\nTab.Accounts        = \\u8D26\\u6237\nTab.Adjust          = \\u8C03\\u6574\nTab.AppLicense      = \\u8BB8\\u53EF\\u534F\\u8BAE\nTab.Budgeting       = \\u9884\\u7B97\nTab.Charge          = \\u8D39\\u7528\nTab.Credit          = \\u8D37\\u65B9\nTab.Credits         = \\u8363\\u8000\\u5C5E\\u4E8E\nTab.DataEngine      = \\u6570\\u636E\\u5F15\\u64CE\nTab.DataProviders   = Data Providers\nTab.Day             = \\u65E5\nTab.Debit           = \\u501F\\u65B9\nTab.Formats         = Formats\nTab.GPLLicense      = GPL\\u8BB8\\u53EF\nTab.General         = \\u5E38\\u89C4\nTab.LGPLLicense     = LGPL\\u8BB8\\u53EF\nTab.License         = \\u8BB8\\u53EF\nTab.Month           = \\u6708\nTab.Network         = \\u7F51\\u7EDC\nTab.None            = \\u65E0\nTab.Payment         = \\u4ED8\\u6B3E\nTab.Register        = \\u767B\\u8BB0\nTab.Reminders       = \\u63D0\\u9192\nTab.Report          = \\u62A5\\u544A\nTab.StartupShutdown = \\u542F\\u52A8 / \\u5173\\u95ED\nTab.SysInfo         = \\u7CFB\\u7EDF\\u4FE1\\u606F\nTab.Transfer        = \\u8F6C\\u8D26\nTab.Week            = \\u5468\nTab.Year            = \\u5E74\n\nTag.Bank                   = \\u94F6\\u884C\nTag.Dividend               = \\u7EA2\\u5229\nTag.FeesOffset             = \\u8D39\\u7528\\u62B5\\u6263\nTag.GainLoss               = \\u635F\\u76CA\nTag.GainsOffset            = \\u6536\\u5165\\u62B5\\u6263\nTag.Investment             = \\u6295\\u8D44\\u8D39\\u7528\nTag.InvestmentCashTransfer = \\u6295\\u8D44\\u73B0\\u91D1\\u8F6C\\u6237\nTag.InvestmentFee          = \\u6295\\u8D44\\u8D39\\u7528\nTag.LTCG                   = \\u957F\\u671F\\u8D44\\u672C\\u6536\\u76CA\\u5206\\u914D\nTag.MTCG                   = \\u4E2D\\u671F\\u8D44\\u672C\\u6536\\u76CA\\u5206\\u914D\nTag.Misc                   = \\u5176\\u4ED6\nTag.NonTaxableInterest     = \\u975E\\u5E94\\u7A0E\\u5229\\u606F\nTag.STCG                   = \\u77ED\\u671F\\u8D44\\u672C\\u6536\\u76CA\\u5206\\u914D\nTag.TaxableInterest        = \\u5E94\\u7A0E\\u5229\\u606F\nTag.Vat                    = \\u589E\\u503C\\u7A0E\n\nTitle.About                      = \\u5173\\u4E8E\nTitle.AccountBalance             = \\u8D26\\u6237\\u4F59\\u989D\nTitle.AccountFilter              = \\u8D26\\u6237\\u7B5B\\u9009\nTitle.AccountGroups              = \\u8D26\\u6237\\u7EC4\nTitle.AccountInfo                = \\u8D26\\u6237\\u4FE1\\u606F\nTitle.AccountRegister            = \\u8D26\\u6237\\u767B\\u8BB0\nTitle.AccountSecurities          = \\u8BC1\\u5238\\u8D26\\u6237\nTitle.AddRemCurr                 = \\u6DFB\\u52A0/\\u5220\\u9664\\u5E01\\u79CD\nTitle.AmortizationSetup          = \\u644A\\u9500\\u8BBE\\u7F6E\nTitle.Archive                    = \\u5B58\\u6863\nTitle.AutoComplete               = \\u81EA\\u52A8\\u5B8C\\u6210\nTitle.AutoSave                   = \\u81EA\\u52A8\\u4FDD\\u5B58\nTitle.Available                  = \\u53EF\\u7528\\u7684\nTitle.BackgroundUpdate           = \\u540E\\u53F0\\u66F4\\u65B0\nTitle.BackingStore               = \\u540E\\u53F0\\u5B58\\u50A8\nTitle.BalanceSheet               = \\u4F59\\u989D\\u8868\nTitle.BaseColor                  = \\u66F4\\u6539\\u57FA\\u672C\\u989C\\u8272\nTitle.BudgetGoal                 = \\u9884\\u7B97\\u76EE\\u6807\nTitle.BudgetManager              = \\u9884\\u7B97\\u7BA1\\u7406\\u5668\nTitle.BudgetProperties           = \\u9884\\u7B97\\u5C5E\\u6027\nTitle.ButtonOrder                = \\u6309\\u94AE\\u987A\\u5E8F\nTitle.ChangePassword             = \\u66F4\\u6539\\u5BC6\\u7801\nTitle.CheckDesign                = \\u8D26\\u5355\\u8BBE\\u8BA1\nTitle.ChooseAccounts             = \\u9009\\u62E9\\u8981\\u521B\\u5EFA\\u7684\\u8D26\\u6237\nTitle.ColVis                     = \\u663E\\u793A\\u5217\nTitle.Colors                     = \\u989C\\u8272\nTitle.CommoditiesSecurities      = \\u5546\\u54C1/\\u8BC1\\u5238\nTitle.ConfigTransImportFilters   = \\u914D\\u7F6E\\u4EA4\\u6613\\u5BFC\\u5165\\u8FC7\\u6EE4\\u5668\nTitle.Confirm                    = \\u786E\\u8BA4\nTitle.ConnectServer              = \\u8FDE\\u63A5\\u5230\\u670D\\u52A1\\u5668\nTitle.Connection                 = \\u8FDE\\u63A5\\u5C5E\\u6027\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = \\u65B0\\u589E/\\u7F16\\u8F91\\u6709\\u4EF7\\u8BC1\\u5238\nTitle.Credits                    = \\u8D37\\u65B9\nTitle.Currencies                 = \\u5E01\\u79CD\nTitle.Current                    = \\u5F53\\u524D\nTitle.CutOffDate                 = \\u9009\\u62E9\\u622A\\u6B62\\u65E5\\u671F\nTitle.DatabaseCfg                = \\u6570\\u636E\\u5E93\\u914D\\u7F6E\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = \\u501F\\u65B9\nTitle.DefDefCurr                 = \\u5B9A\\u4E49\\u9ED8\\u8BA4\\u8D27\\u5E01\nTitle.DefTranNum                 = \\u9ED8\\u8BA4\\u4EA4\\u6613\\u53F7\\u7801\nTitle.DefaultBehavior            = \\u9ED8\\u8BA4\\u884C\\u4E3A\nTitle.Defaults                   = \\u9ED8\\u8BA4\\u503C\nTitle.DeleteAttachment           = \\u5220\\u9664\\u9644\\u4EF6\nTitle.Display                    = \\u663E\\u793A\nTitle.DuplicateTransaction       = \\u521B\\u5EFA\\u4EA4\\u6613\\u526F\\u672C\nTitle.DuplicateTransactionsFound = \\u53D1\\u73B0\\u91CD\\u590D\\u4EA4\\u6613\nTitle.EditExchangeRates          = \\u7F16\\u8F91\\u6C47\\u7387\nTitle.EndMonthBalance            = \\u6708\\u7EC8\\u8D26\\u6237\\u4F59\\u989D\nTitle.EnterPassword              = \\u8F93\\u5165\\u5BC6\\u7801\nTitle.Entry                      = \\u4EA4\\u6613\\u6761\\u76EE\nTitle.Error                      = \\u9519\\u8BEF\nTitle.EventHistory               = \\u4E8B\\u4EF6\\u5386\\u53F2\nTitle.ExchangeRate               = \\u6C47\\u7387\nTitle.FileImport                 = \\u9009\\u62E9\\u9700\\u5BFC\\u5165\\u7684\\u6587\\u4EF6\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = \\u8FC7\\u6EE4\\u5668\nTitle.FontSize                   = \\u4FEE\\u6539\\u9ED8\\u8BA4\\u5B57\\u4F53\\u5927\\u5C0F\nTitle.Fonts                      = \\u9ED8\\u8BA4\\u5B57\\u4F53\nTitle.Frequency                  = \\u9891\\u7387\nTitle.HTTPProxy                  = HTTP\\u4EE3\\u7406\\u670D\\u52A1\\u5668\nTitle.Help                       = jGnash\\u5E2E\\u52A9\nTitle.HistoryImport              = \\u5BFC\\u5165\\u5386\\u53F2\\u6570\\u636E\nTitle.ImageFiles                 = \\u56FE\\u50CF\\u6587\\u4EF6\nTitle.ImpPartQif                 = \\u5BFC\\u5165\\u90E8\\u5206 QIF \\u6587\\u4EF6\nTitle.ImpSum                     = \\u5BFC\\u5165\\u6458\\u8981\\u8BF4\\u660E\nTitle.ImportOFX                  = \\u5BFC\\u5165 OFX \\u6587\\u4EF6\nTitle.ImportTransactions         = \\u4ECE\\u6587\\u4EF6\\u5BFC\\u5165\\u4EA4\\u6613\nTitle.IncomeExpenseBarChart      = \\u6536\\u652F\\u67F1\\u72B6\\u56FE\nTitle.IncomeExpenseChart         = \\u6536\\u652F\\u997C\\u72B6\\u56FE\nTitle.Information                = \\u4FE1\\u606F\nTitle.InvFees                    = \\u6295\\u8D44\\u8D39\\u7528\nTitle.InvGainsLoss               = \\u6295\\u8D44\\u635F\\u76CA\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = \\u4FEE\\u6539\\u4EA4\\u6613\nTitle.ModOFXTrans                = \\u4FEE\\u6539 OFX \\u4EA4\\u6613\nTitle.ModQIFTrans                = \\u4FEE\\u6539 QIF \\u4EA4\\u6613\nTitle.ModifyAccount              = \\u4FEE\\u6539\\u8D26\\u6237\nTitle.ModifyCurrencies           = \\u4FEE\\u6539\\u8D27\\u5E01\nTitle.ModifyReminder             = \\u4FEE\\u6539\\u63D0\\u9192\nTitle.ModifySecHistory           = \\u4FEE\\u6539\\u8BC1\\u5238\\u5386\\u53F2\\u6570\\u636E\nTitle.ModifyTransaction          = \\u4FEE\\u6539\\u4EA4\\u6613\nTitle.MoveFile                   = \\u79FB\\u52A8\\u6587\\u4EF6\nTitle.NewAccount                 = \\u65B0\\u5EFA\\u8D26\\u6237\nTitle.NewBudget                  = \\u65B0\\u5EFA\\u9810\\u7B97\nTitle.NewFile                    = \\u65B0\\u5EFA\\u6587\\u4EF6\nTitle.NewPassword                = \\u65B0\\u5BC6\\u7801\nTitle.NewReminder                = \\u65B0\\u5EFA\\u63D0\\u9192\nTitle.NewTrans                   = \\u65B0\\u589E\\u4EA4\\u6613\nTitle.Notes                      = \\u5907\\u6CE8\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = \\u6253\\u5F00\nTitle.Options                    = \\u9009\\u9879\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = \\u9875\\u9762\\u8BBE\\u7F6E\nTitle.ParentAccount              = \\u7236\\u8D26\\u6237\nTitle.PercentDist                = \\u9500\\u552E\\u767E\\u5206\\u6BD4\nTitle.PercentExpense             = \\u652F\\u51FA\\u767E\\u5206\\u6BD4\nTitle.PercentIncome              = \\u6536\\u5165\\u767E\\u5206\\u6BD4\nTitle.PleaseWait                 = \\u8BF7\\u7A0D\\u7B49\nTitle.PortfolioReport            = \\u8D44\\u4EA7\\u7EC4\\u5408\\u62A5\\u544A\nTitle.PriceHistory               = \\u4EF7\\u683C\\u5386\\u53F2\nTitle.ProfitLoss                 = \\u8D44\\u4EA7\\u8D1F\\u503A\\u8868\nTitle.ReconcileSettings          = \\u5BF9\\u8D26\\u8C03\\u6574\\u8BBE\\u7F6E\nTitle.Reminder                   = \\u63D0\\u9192\nTitle.Reminders                  = \\u63D0\\u9192\nTitle.RenameBudget               = \\u9884\\u7B97\\u6539\\u540D\nTitle.ReportOptions              = \\u62A5\\u544A\\u9009\\u9879\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = \\u7559\\u5B58\\u6536\\u76CA\nTitle.ReverseAccountBalances     = \\u7EA2\\u5B57\\u8D1F\\u503C\\u663E\\u793A\\u8D26\\u6237\\u4F59\\u989D\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = \\u53E6\\u5B58\\u4E3A\nTitle.SaveFile                   = \\u4FDD\\u5B58\\u6587\\u4EF6\nTitle.SecurityHistory            = \\u8BC1\\u5238\\u5386\\u53F2\\u6570\\u636E\nTitle.SelAccount                 = \\u9009\\u62E9\\u8D26\\u6237\nTitle.SelAvailCurr               = \\u9009\\u62E9\\u53EF\\u7528\\u8D27\\u5E01\nTitle.SelDate                    = \\u9009\\u62E9\\u65E5\\u671F\nTitle.SelDefCurr                 = \\u9009\\u62E9\\u9ED8\\u8BA4\\u8D27\\u5E01\nTitle.SelDefLocale               = \\u9009\\u62E9\\u9ED8\\u8BA4\\u8BED\\u8A00\nTitle.SelDestAccount             = \\u9009\\u62E9\\u76EE\\u6807\\u8D26\\u6237\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = \\u9009\\u62E9\\u6743\\u76CA\\u8D26\\u6237\nTitle.SelFile                    = \\u9009\\u62E9\\u6587\\u4EF6\nTitle.SelQifDateFormat           = \\u9009\\u62E9 QIF \\u65E5\\u671F\\u683C\\u5F0F\nTitle.SelectColor                = \\u9009\\u62E9\\u989C\\u8272\nTitle.Selected                   = \\u9009\\u4E2D\\u7684\nTitle.Shutdown                   = \\u5173\\u95ED\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = \\u62C6\\u5206\\u4EA4\\u6613\nTitle.Startup                    = \\u542F\\u52A8\nTitle.Steps                      = \\u6B65\\u9AA4\nTitle.Success                    = \\u6210\\u529F\nTitle.Summary                    = \\u6982\\u8981\nTitle.TagManager=Tag Manager\nTitle.Terms                      = \\u8BCD\\u6C47\nTitle.Transaction                = \\u4EA4\\u6613\nTitle.TransactionImport          = \\u4EA4\\u6613\\u5BFC\\u5165\nTitle.TransactionList            = \\u4EA4\\u6613\\u5217\\u8868\nTitle.TransactionSetup           = \\u4EA4\\u6613\\u8BBE\\u5B9A\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = \\u67E5\\u770B\\u56FE\\u7247\nTitle.Visible                    = \\u53EF\\u89C1\\u7684\nTitle.VisibleAccountTypes        = \\u53EF\\u89C1\\u8D26\\u6237\\u7C7B\\u578B\nTitle.Warning                    = \\u8B66\\u544A\n\nToolTip.AccountList                          = \\u8D26\\u6237\\u5217\\u8868\nToolTip.AccountRegister                      = \\u767B\\u8BB0\\u8D26\\u6237\nToolTip.AddAttachment                        = \\u6DFB\\u52A0\\u9644\\u4EF6\nToolTip.BudgetMgr                            = \\u589E\\u52A0\\uFF0C\\u5220\\u9664\\uFF0C\\u4FEE\\u6539\\u9884\\u7B97\nToolTip.Budgeting                            = \\u9884\\u7B97\\u89C6\\u56FE\nToolTip.ColumnVis                            = \\u6539\\u53D8\\u663E\\u793A\\u7684\\u5217\nToolTip.ConvertSEntry                        = \\u5C06\\u6B64\\u6761\\u4EA4\\u6613\\u8F6C\\u6362\\u4E3A\\u590D\\u5F0F\\u4EA4\\u6613\nToolTip.DeleteAccount                        = \\u5220\\u9664\\u8D26\\u6237\nToolTip.DeleteAllExceptFridaySecurityHistory = \\u5220\\u9664\\u5468\\u4E94\\u5916\\u7684\\u5176\\u4ED6\\u8BC1\\u5238\\u5386\\u53F2\\u4FE1\\u606F\nToolTip.DeleteAllExceptMondaySecurityHistory = \\u5220\\u9664\\u5468\\u4E00\\u5916\\u7684\\u5176\\u4ED6\\u8BC1\\u5238\\u5386\\u53F2\\u4FE1\\u606F\nToolTip.DeleteAttachment                     = \\u5220\\u9664\\u9644\\u4EF6\nToolTip.DeleteWeekendSecurityHistory         = \\u5220\\u9664\\u5468\\u516D\\u65E5\\u7684\\u8BC1\\u5238\\u5386\\u53F2\\u4FE1\\u606F\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = \\u5BFC\\u51FA\\u9009\\u4E2D\\u7684\\u4EA4\\u6613\\u5230CSV\\u6216OFX\\u6587\\u4EF6\nToolTip.FilterAccount                        = \\u7B5B\\u9009\\u8D26\\u6237\nToolTip.FilterAccounts                       = \\u4F9D\\u8D26\\u6237\\u7C7B\\u578B\\u7B5B\\u9009\nToolTip.FilterMemo                           = \\u4F9D\\u5907\\u5FD8\\u7B5B\\u9009\nToolTip.FilterPayee                          = \\u4F9D\\u652F\\u4ED8\\u4EBA\\u7B5B\\u9009\nToolTip.FilterReconciledState                = \\u4F9D\\u5BF9\\u8D26\\u72B6\\u6001\\u7B5B\\u9009\nToolTip.FilterTransactionAge                 = \\u4F9D\\u4EA4\\u6613\\u65F6\\u957F\\u7B5B\\u9009\nToolTip.FontSize                             = \\u4FEE\\u6539\\u5B57\\u4F53\\u5927\\u5C0F\nToolTip.FuzzyMatch                           = \\u5339\\u914D\\u57FA\\u4E8E\\u6700\\u540E\\u4E00\\u4E2A\\u76F8\\u4F3C\\u8BB0\\u5F55\nToolTip.Help                                 = \\u5E2E\\u52A9\nToolTip.ISIN                                 = \\u56FD\\u9645\\u8BC1\\u5238\\u8BC6\\u522B\\u7F16\\u7801\nToolTip.IntegersOnly                         = \\u53EA\\u5141\\u8BB8\\u6574\\u6570\nToolTip.ModifyAccount                        = \\u4FEE\\u6539\\u8D26\\u6237\nToolTip.NewAccount                           = \\u65B0\\u589E\\u8D26\\u6237\nToolTip.PageSetup                            = \\u9875\\u9762\\u8BBE\\u7F6E\nToolTip.PrintRegRep                          = \\u6253\\u5370\\u767B\\u8BB0\\u62A5\\u544A\nToolTip.ReconcileAccount                     = \\u5BF9\\u8D26/\\u8C03\\u8282\\u8D26\\u6237\nToolTip.Reminders                            = \\u63D0\\u9192\nToolTip.ResizeColumns                        = \\u8C03\\u6574\\u5217\\u5BBD\nToolTip.ReversedCredit                       = \\u53CD\\u8F6C\\u4FE1\\u8D37\\u4EFB\\u4F55\\u8D26\\u6237\\u7684\\u6B63\\u8D1F\\u53F7\nToolTip.Scale                                = \\u4F4D\\u6570\\u5230\\u5C0F\\u6570\\u70B9\\u53F3\\u8FB9\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = \\u663E\\u793A\\u660E\\u7EC6\nToolTip.Timestamp                            = \\u65F6\\u95F4\\u6233\nToolTip.ViewAttachment                       = \\u67E5\\u770B\\u9644\\u4EF6\nToolTip.ZoomRegister                         = \\u6253\\u5F00\\u65B0\\u8D26\\u6237\\u767B\\u8BB0\n\nTransaction.AddShare        = \\u6DFB\\u52A0\\u80A1\\u7968\nTransaction.BuyShare        = \\u8D2D\\u4E70\\u80A1\\u7968\nTransaction.Dividend        = \\u7EA2\\u5229\nTransaction.DoubleEntry     = \\u590D\\u5F0F\\u8BB0\\u5E10\nTransaction.MergeShare      = \\u5408\\u5E76\\u80A1\\u7968\nTransaction.ReinvestDiv     = \\u7EA2\\u5229\\u518D\\u6295\\u8D44\nTransaction.RemoveShare     = \\u79FB\\u9664\\u80A1\\u7968\nTransaction.ReturnOfCapital = \\u8D44\\u672C\\u56DE\\u62A5\nTransaction.SellShare       = \\u5356\\u51FA\\u80A1\\u7968\nTransaction.SingleEntry     = \\u7B80\\u5F0F\\u8BB0\\u5E10\nTransaction.Split           = \\u62C6\\u5206\\u4EA4\\u6613\nTransaction.SplitEntry      = \\u62C6\\u5206\\u4EA4\\u6613\\u8BB0\\u5F55\nTransaction.SplitShare      = \\u62C6\\u5206\\u80A1\\u7968\nTransaction.TransferIn      = \\u8F6C\\u8D26\\u5165\nTransaction.TransferOut     = \\u8F6C\\u8D26\\u51FA\n\nWord.Add             = \\u6DFB\\u52A0\nWord.All             = \\u5168\\u90E8\nWord.Balance         = \\u4F59\\u989D\nWord.Buy             = \\u4E70\\u5165\nWord.Commodity       = \\u5546\\u54C1\nWord.Copy            = \\u526F\\u672C\nWord.Description     = \\u63CF\\u8FF0\nWord.Difference      = \\u5DEE\\u989D\nWord.Dividend        = \\u5206\\u7EA2\nWord.Exchange        = \\u5151\\u6362\nWord.Fees            = \\u8D39\\u7528\nWord.GrossExpense    = \\u6BDB\\u652F\\u51FA\nWord.GrossIncome     = \\u6BDB\\u6536\\u5165\nWord.Interest        = \\u5229\\u606F\nWord.Into            = \\u4E3A\nWord.Invalid         = \\u65E0\\u6548\nWord.Merge           = \\u53BB\nWord.Monthly         = \\u6309\\u6708\\u4EFD\nWord.Name            = \\u540D\\u5B57\nWord.NetIncome       = \\u51C0\\u6536\\u5165\nWord.NetWorth        = \\u51C0\\u503C\nWord.NewBudget       = \\u65B0\\u9884\\u7B97\nWord.None            = \\u65E0\nWord.Quarterly       = \\u6309\\u5B63\\u5EA6\nWord.ReInvDiv        = \\u590D\\u5229\\u8BA1\\u606F\nWord.Remove          = \\u79FB\\u9664\nWord.ReturnOfCapital = \\u8D44\\u672C\\u56DE\\u62A5\nWord.Seconds         = \\u79D2\nWord.Security        = \\u8BC1\\u5238\nWord.Sell            = \\u5356\\u51FA\nWord.Split           = \\u62C6\\u80A1\nWord.Subtotal        = \\u5C0F\\u8BA1\nWord.Total           = \\u5408\\u8BA1\nWord.Totals          = \\u5408\\u8BA1\nWord.Yearly          = \\u6309\\u5E74\\u5EA6\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/resource_zh_TW.properties",
    "content": "#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)\n\nAccountType.Asset            = \\u8CC7\\u7522\nAccountType.Bank             = \\u9280\\u884C\nAccountType.Cash             = \\u73FE\\u91D1\nAccountType.Checking         = \\u652F\\u7968\nAccountType.Credit           = \\u4FE1\\u7528\\u5361\nAccountType.Equity           = Equity/\\u6B0A\\u76CA\nAccountType.Expense          = \\u652F\\u51FA\nAccountType.Income           = \\u6536\\u5165\nAccountType.Investment       = \\u6295\\u8CC7\nAccountType.Liability        = \\u8CA0\\u50B5\nAccountType.MoneyMarket      = \\u8CA8\\u5E63\\u5E02\\u5834\\u5E33\\u6236\nAccountType.Mutual           = Mutual Fund/\\u57FA\\u91D1\nAccountType.Root             = Root\nAccountType.SimpleInvestment = \\u7C21\\u55AE\\u6295\\u8CC7\n\nButton.AccTerms                = \\u4F7F\\u7528\\u6703\\u8A08\\u540D\\u8A5E\nButton.Accounts                = \\u5E33\\u6236\nButton.AckSel                  = \\u78BA\\u8A8D\\u9078\\u64C7\nButton.Add                     = \\u65B0\\u589E\nButton.AllDates                = \\u6240\\u6709\\u65E5\\u671F\nButton.Amortize                = \\u6524\\u9084\nButton.AnimationsEnabled       = \\u555F\\u7528\\u52D5\\u756B\\u6548\\u679C\nButton.AnyStatus               = \\u4EFB\\u4F55\\u72C0\\u614B\nButton.Apply                   = \\u78BA\\u5B9A\nButton.AssetAccounts           = \\u8CC7\\u7522\\u5E33\\u6236\nButton.AutoReconcile           = \\u81EA\\u52D5\\u5C0D\\u5E33\\u8ABF\\u7BC0\\u53CA\\u5206\\u62C6\\u4EA4\\u6613/Automatically Reconcile Split Transactions\nButton.AutoResizeColumns       = \\u81EA\\u52A8\\u8C03\\u6574\\u8868\\u683C\\u5217\\u5BBD\nButton.AvailSecurities         = \\u53EF\\u7528\\u7684\\u8BC1\\u5238\nButton.Back                    = \\u9000\\u5F8C\nButton.BankAccounts            = \\u9280\\u884C\\u5E33\\u865F\nButton.BudgetMgr               = \\u9810\\u7B97\\u7BA1\\u7406\\u5668\nButton.Budgeting               = \\u9810\\u7B97\nButton.CalcBal                 = Calculate Balance\nButton.Cancel                  = \\u53D6\\u6D88\nButton.CheckForUpdates         = \\u6AA2\\u67E5\\u66F4\\u65B0\nButton.CheckReminders          = \\u6AA2\\u67E5\\u63D0\\u9192\\u9805\\u76EE/Check Reminders\nButton.Clear                   = \\u6E05\\u9664\nButton.ClearAll                = \\u5168\\u90E8\\u6E05\\u9664\nButton.Cleared                 = \\u5DF2\\u7D50\\u6E05\nButton.Close                   = \\u95DC\\u9589\nButton.Compare                 = \\u6BD4\\u8F03\nButton.ConcatenateMemos        = \\u7D44\\u5408\\u5099\\u5FD8\\u9304\nButton.ConfirmReminderDelete   = \\u78BA\\u8A8D\\u522A\\u9664\\u63D0\\u9192\nButton.ConfirmTransDelete      = \\u78BA\\u8A8D\\u522A\\u9664\\u4EA4\\u6613\nButton.CopyToClip              = \\u8907\\u88FD\\u5230\\u526A\\u8CBC\\u677F\nButton.CreateTimeFile          = \\u9000\\u51FA\\u6642\\u5275\\u5EFA\\u5E36\\u6642\\u9593\\u6233\\u7684\\u6587\\u4EF6\nButton.CreditAccounts          = \\u4FE1\\u7528\\u5E33\\u6236\nButton.Delete                  = \\u522A\\u9664\nButton.DeleteAll               = \\u522A\\u9664\\u5168\\u90E8\nButton.DeleteWeekends          = \\u522A\\u9664\\u9031\\u672B\nButton.DetailSplits            = \\u663E\\u793A\\u62C6\\u5206\\u7EC6\\u8282\nButton.Duplicate               = \\u8907\\u88FD\nButton.Edit                    = \\u7DE8\\u8F2F\nButton.EnableAutoComplete      = \\u5141\\u8A31\\u81EA\\u52D5\\u5B8C\\u6210\nButton.Enabled                 = \\u5141\\u8A31\nButton.EndingBalance           = \\u671F\\u672B\\u9918\\u984D\nButton.Enter                   = \\u5F55\\u5165\nButton.EnterDaysBefore         = \\u81EA\\u52D5\\u5728\\u6578\\u5929\\u524D\\u8F38\\u5165\nButton.ExcludeFromBudget       = \\u5F9E\\u9810\\u7B97\\u4E2D\\u6392\\u9664\nButton.ExecuteNow              = \\u7ACB\\u5373\\u6267\\u884C\nButton.ExpenseAccounts         = \\u652F\\u51FA\\u5E33\\u6236\nButton.Export                  = \\u5BFC\\u51FA\\u4EA4\\u6613\nButton.ExportSpreadsheet       = \\u5C0E\\u51FA\\u96FB\\u5B50\\u8868\\u683C\nButton.Filter                  = \\u7BE9\\u9078\nButton.Finish                  = \\u5B8C\\u6210\nButton.FinishLater             = \\u7A0D\\u5F8C\\u5B8C\\u6210\nButton.ForceDefaultCurrency    = \\u5F37\\u5236\\u4F7F\\u7528\\u9ED8\\u8A8D\\u8CA8\\u5E63\nButton.ForceGC                 = \\u5F3A\\u5236\\u4F18\\u5316\\u5185\\u5B58\nButton.HTTPAuth                = Proxy \\u9700\\u8981\\u6388\\u6B0A\nButton.Hidden                  = \\u96B1\\u85CF\nButton.HideAccount             = \\u96B1\\u85CF\\u5E33\\u6236\nButton.HideLockedAccount       = \\u96B1\\u85CF\\u9396\\u5B9A\\u7684\\u5E33\\u6236\nButton.HidePlaceholderAccount  = \\u96B1\\u85CF\\u4F54\\u4F4D\\u7B26\\u5E33\\u6236\nButton.HideZeroBalance         = \\u96B1\\u85CF\\u96F6\\u9918\\u984D\\u5E10\\u6237\nButton.HistoricalFill          = \\u6B77\\u53F2\\u586B\\u5145\nButton.Horizontal              = \\u6A6B\\u5411\nButton.IncludeSubAccounts      = \\u5305\\u62EC\\u5B50\\u8D26\\u6237\nButton.IncomeAccounts          = \\u6536\\u5165\\u5E33\\u6236\nButton.IncomeAndExpense        = \\u6536\\u5165\\u548C\\u652F\\u51FA\nButton.Insert                  = \\u63D2\\u5165\nButton.InvertBalances          = Invert Balances\nButton.InvertSelection         = \\u53CD\\u5411\\u9078\\u64C7\nButton.Jump                    = \\u8DF3\\u5165\nButton.KeepFridays             = \\u4FDD\\u7559\\u661F\\u671F\\u4E94\nButton.KeepMondays             = \\u4FDD\\u7559\\u661F\\u671F\\u4E00\nButton.Landscape               = Landscape\nButton.Last120Days             = \\u6700\\u8FD1120\\u5929\nButton.Last12Months            = \\u6700\\u8FD112\\u500B\\u6708\nButton.Last30Days              = \\u6700\\u8FD130\\u5929\nButton.Last60Days              = \\u6700\\u8FD160\\u5929\nButton.Last90Days              = \\u6700\\u8FD190\\u5929\nButton.LiabilityAccounts       = \\u8CA0\\u50B5\\u8D26\\u6237\nButton.Locked                  = \\u9396\\u5B9A\nButton.MatchAccountOnly        = \\u50C5\\u4F7F\\u7528\\u5E33\\u6236\\u7684\\u7279\\u5B9A\\u4EA4\\u6613\\u9032\\u884C\\u5339\\u914D\nButton.MatchAllTrans           = \\u4F7F\\u7528\\u6240\\u6709\\u4EA4\\u6613\\u9032\\u884C\\u5339\\u914D\nButton.MatchCaseSensitive      = \\u5339\\u914D\\u65F6\\u5340\\u5206\\u5927\\u5C0F\\u5BEB\nButton.Modify                  = \\u7DE8\\u8F2F\nButton.MonthlyBalance          = \\u6BCF\\u6708\\u9918\\u984D\nButton.New                     = \\u65B0\\u589E\nButton.NewEmpty                = \\u65B0\\u589E\\u7A7A\\u767D\\u9884\\u7B97\nButton.NewHist                 = \\u65B0\\u589E\\u7A7A\\u767D\\u9884\\u7B97\nButton.NewPayment              = \\u65B0\\u589E\\u4ED8\\u6B3E\nButton.Next                    = \\u4E0B\\u4E00\\u7B46\nButton.No                      = \\u5426\nButton.NoEndDate               = \\u7121\\u7D50\\u675F\\u65E5\\u671F\nButton.None                    = \\u6CA1\\u6709\nButton.NotReconciled           = \\u672A\\u5C0D\\u5E33\nButton.Ok                      = \\u662F\nButton.Open                    = \\u958B\\u555F\nButton.OpenLastOnStartup       = \\u555F\\u52D5\\u6642\\u6253\\u958B\\u4E0A\\u6B21\\u7684\\u6587\\u4EF6\nButton.Options                 = Options\nButton.Order.LinuxOS           = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Linux)\nButton.Order.MacOS             = No, Yes, [Cancel | Close | Clear], [OK | Enter] (Mac)\nButton.Order.WindowsOS         = Yes, No, [OK | Enter], [Cancel | Close | Clear] (Windows)\nButton.PageSetup               = \\u7248\\u9762\\u8A2D\\u5B9A\nButton.PlaceHolder             = \\u865B\\u64EC\\u5E33\\u6236/Placeholder\nButton.Portrait                = Portrait\nButton.Print                   = \\u5217\\u5370\nButton.PrintSample             = \\u5217\\u5370\\u7BC4\\u672C\nButton.Properties              = \\u5C6C\\u6027\nButton.Reconcile               = \\u5C0D\\u5E33/\\u8ABF\\u7BC0\nButton.ReconcileBoth           = All transaction accounts have same reconciled state\nButton.ReconcileDisable        = \\u7981\\u7528\\u81EA\\u52A8\\u5BF9\\u8D26\\u529F\\u80FD\nButton.ReconcileIncomeExpense  = \\u81EA\\u52A8\\u5BF9\\u6536\\u652F\\u8D26\\u6237\\u5BF9\\u8D26\nButton.Reconciled              = \\u5B8C\\u6210\\u5C0D\\u5E33\\u8ABF\\u7BC0\nButton.Refresh                 = \\u5237\\u65B0\nButton.RegDate                 = \\u5132\\u5B58\\u6700\\u5F8C\\u4EA4\\u6613\\u65E5\\u671F\nButton.Register                = \\u767B\\u9304\nButton.RegisterFollowsList     = Register Follows Account List\nButton.RememberPassword        = \\u8BB0\\u4F4F\\u5BC6\\u7801\nButton.RemindLater             = \\u7A0D\\u5F8C\\u63D0\\u9192\nButton.Reminders               = \\u63D0\\u9192\nButton.RemoteServer            = \\u8FDC\\u7A0B\\u670D\\u52A1\\u5668\nButton.Remove                  = \\u79FB\\u9664\nButton.RemoveOldBackups        = \\u5220\\u9664\\u65E7\\u7684\\u5907\\u4EFD\nButton.Rename                  = \\u66F4\\u540D\nButton.ReportDate              = Remember last report date\nButton.ResetAll                = \\u5168\\u90E8\\u91CD\\u7F6E\nButton.Resize                  = \\u8ABF\\u6574\\u5927\\u5C0F\nButton.RestoreDefault          = \\u6062\\u590D\\u4E3A\\u9ED8\\u8BA4\nButton.RestoreLastTranTab      = \\u6062\\u590D\\u4E0A\\u6B21\\u4F7F\\u7528\\u7684\\u4EA4\\u6613\\u5206\\u9875\nButton.RoundToWhole            = Round up to whole amounts\nButton.RunningBalance          = \\u52A8\\u6001\\u5E73\\u8861\nButton.Save                    = \\u5132\\u5B58/Save\nButton.SaveFilters             = \\u4FDD\\u5B58\\u8FC7\\u6EE4\\u5668\nButton.SaveImage               = \\u4FDD\\u5B58\\u56FE\\u50CF\nButton.Select                  = \\u9078\\u64C7\nButton.SelectAll               = \\u9078\\u64C7\\u5168\\u90E8\nButton.SelectText              = \\u83B7\\u5F97\\u7126\\u70B9\\u65F6\\u9009\\u4E2D\\u6587\\u672C\nButton.ShowCommodities         = \\u986F\\u793A\\u6240\\u6709\\u9805\\u76EE\nButton.ShowEmptyAccounts       = \\u986F\\u793A\\u96F6\\u9918\\u984D\\u5E33\\u6236\nButton.ShowPercentValues       = \\u663E\\u793A\\u767E\\u5206\\u6BD4\nButton.ShowTimestamp           = \\u663E\\u793A\\u65F6\\u95F4\\u6233\nButton.Splits                  = \\u62C6\\u5206\nButton.Start                   = \\u5F00\\u59CB\nButton.Stop                    = \\u505C\\u6B62\nButton.SubstanceAnimations     = \\u5F00\\u542F\\u6F02\\u4EAE\\u754C\\u9762\\u52A8\\u753B\\uFF08Substance Look and Feel\\uFF09\nButton.SumColVis               = \\u663E\\u793A\\u603B\\u8BA1\\u5217\nButton.SumRowVis               = \\u663E\\u793A\\u6C47\\u603B\\u884C\nButton.ThemesEnabled           = \\u4F7F\\u7528\\u4F48\\u666F\\u4E3B\\u984C\nButton.Timestamp               = \\u65E5\\u671F\\u8A18\\u865F/timestamp\nButton.Today                   = \\u4ECA\\u5929\nButton.UpdateCurrenciesStartup = \\u542F\\u52A8\\u65F6\\u66F4\\u65B0\\u8D27\\u5E01\\u6C47\\u7387\nButton.UpdateOnline            = \\u7DDA\\u4E0A\\u66F4\\u65B0/Update Online\nButton.UpdateSecuritiesStartup = \\u542F\\u52A8\\u65F6\\u66F4\\u65B0\\u8BC1\\u5238\\u4FE1\\u606F\nButton.UseDailyRate            = \\u4F7F\\u7528\\u65E5\\u9031\\u671F\\u8CBB\\u7387/Use Daily Periodic Rate\nButton.UseEncryption           = \\u52A0\\u5BC6/Use Encryption\nButton.UseFuzzyMatch           = \\u4F7F\\u7528\\u6A21\\u7CCA\\u5339\\u914D\nButton.UseLongNames            = \\u4F7F\\u7528\\u9577\\u540D\\u7A31/Use Long Names\nButton.UseProxy                = \\u4F7F\\u7528\\u4EE3\\u7406\\u4F3A\\u670D\\u5668/Use Proxy\nButton.UseRegexForFilter       = \\u5C0D\\u904E\\u6FFE\\u5668\\u4F7F\\u7528\\u6B63\\u5247\\u8868\\u9054\\u5F0F\nButton.Vertical                = \\u7E31\\u5411/Vertical\nButton.Yes                     = \\u662F\nButton.Zoom                    = \\u7E2E\\u653E\n\nColumn.Account                        = \\u5E33\\u6236\nColumn.AccountName                    = \\u5E33\\u6236\\u540D\\u7A31\nColumn.Action                         = \\u52D5\\u4F5C/Action\nColumn.Actual                         = \\u5B9E\\u9645\\u503C\nColumn.Amount                         = \\u91D1\\u984D\nColumn.Approve                        = \\u6838\\u51C6/Approve\nColumn.Balance                        = \\u9918\\u984D\nColumn.Budgeted                       = \\u9884\\u7B97\\u503C\nColumn.Charge                         = \\u624B\\u7E8C\\u8CBB/Charge\nColumn.Close                          = \\u6536\\u76D8\\u4EF7\nColumn.Clr                            = \\u5C0D\\u5E33\\u8ABF\\u7BC0/Clr\nColumn.Code                           = \\u79D1\\u76EE\\u4EE3\\u7801\nColumn.Commodity                      = \\u5546\\u54C1\nColumn.CostBasis                      = \\u6210\\u672C\\u57FA\\u7840\nColumn.Credit                         = \\u8CB8\\u65B9\nColumn.Currency                       = \\u8D27\\u5E01\nColumn.Date                           = \\u65E5\\u671F\nColumn.Day                            = \\u65E5\nColumn.Debit                          = \\u501F\\u65B9\nColumn.Decrease                       = \\u6E1B\\u5C11\nColumn.Deposit                        = \\u5B58\\u6B3E\nColumn.Description                    = \\u8AAA\\u660E\nColumn.Due                            = \\u622A\\u6B62\nColumn.Enabled                        = \\u542F\\u7528\nColumn.Entries                        = \\u4EA4\\u6613\\u7B46\\u6578\nColumn.Event                          = \\u4E8B\\u4EF6\nColumn.ExchangeRate                   = \\u6C47\\u7387\nColumn.Expense                        = \\u652F\\u51FA\nColumn.Freq                           = \\u983B\\u7387\nColumn.Gain                           = \\u5229\\u6DA6\nColumn.High                           = \\u9AD8\\u50F9\nColumn.Income                         = \\u6536\\u5165\nColumn.Increase                       = \\u589E\\u52A0\nColumn.Investment                     = \\u6295\\u8CC7\nColumn.LastPosted                     = \\u4E0A\\u6B21\\u63D0\\u9192\nColumn.Loss                           = \\u635F\\u5931\nColumn.Low                            = \\u4F4E\\u50F9\nColumn.Memo                           = \\u5099\\u8A3B\nColumn.MktValue                       = \\u5E02\\u4EF7\nColumn.Month                          = \\u6708\nColumn.Num                            = \\u5E8F\\u865F\nColumn.Payee                          = \\u6536\\u6B3E\\u4EBA\nColumn.Payment                        = \\u4ED8\\u6B3E/\\u51FA\\u5E33\nColumn.Percentile                     = Percentile\nColumn.Period                         = \\u5468\\u671F\nColumn.Price                          = \\u50F9\\u683C\nColumn.Print                          = \\u5217\\u5370\nColumn.PropName                       = \\u5C5E\\u6027\\u540D\\u79F0\nColumn.PropVal                        = \\u5C5E\\u6027\\u503C\nColumn.Quantity                       = \\u6578\\u91CF\nColumn.Rebate                         = \\u6298\\u6263\nColumn.Receive                        = \\u6536\\u6B3E/\\u5165\\u5E33\nColumn.ReconciledBalance              = \\u5DF2\\u5BF9\\u8D26\\u4F59\\u989D\nColumn.Remaining                      = \\u5269\\u4F59\\u503C\nColumn.Script                         = Script\nColumn.Security                       = Security\nColumn.Short.InternalRateOfReturn     = IRR\nColumn.Short.PercentagePortfolio      = Portfolio %\nColumn.Short.Quantity                 = Qty\nColumn.Short.RealizedGain             = R Gain\nColumn.Short.RealizedGainPercentage   = R Gain %\nColumn.Short.TotalGain                = T Gain\nColumn.Short.TotalGainPercentage      = T Gain %\nColumn.Short.UnrealizedGain           = U Gain\nColumn.Short.UnrealizedGainPercentage = U Gain %\nColumn.Spend                          = \\u652F\\u51FA/\\u6D88\\u8CBB\nColumn.Timestamp                      = \\u6642\\u9593\\u6233\nColumn.Total                          = \\u5408\\u8A08\nColumn.TotalCostBasis                 = Total CB\nColumn.Type                           = \\u985E\\u578B\nColumn.Value                          = \\u4EF7\\u503C\nColumn.Volume                         = \\u4EA4\\u6613\\u91CF\nColumn.Withdrawal                     = \\u63D0\\u6B3E\n\nDataStoreType.Bxds = \\u4E8C\\u9032\\u88FD\\u6587\\u4EF6\nDataStoreType.H2   = H2\\u5173\\u7CFB\\u578B\\u6570\\u636E\\u5E93\nDataStoreType.HSQL = HyperSQL\\u5173\\u7CFB\\u578B\\u6570\\u636E\\u5E93\nDataStoreType.XML  = XML\\u6587\\u4EF6\n\nItem.Amount         = \\u91D1\\u984D/\\u6578\\u91CF/Amount\nItem.CashDeposit    = \\u73FE\\u91D1\\u5B58\\u6B3E\nItem.CashWithdrawal = \\u73FE\\u91D1\\u63D0\\u6B3E\nItem.Date           = \\u65E5\\u671F/Date\nItem.EFT            = \\u96FB\\u5B50\\u4EA4\\u6613/EFT\nItem.Memo           = \\u5099\\u8A3B\nItem.NextNum        = \\u4E0B\\u4E00\\u865F/Next #\nItem.Payee          = \\u6536\\u6B3E\\u4EBA\nItem.Trans          = \\u8F49\\u5E33/Trans\n\nLabel.AccentColor         = \\u5F3A\\u8C03\\u8272\\uFF1A\nLabel.Account             = \\u5E33\\u6236:\nLabel.AccountCode         = \\u79D1\\u76EE\\u4EE3\\u78BC:\nLabel.AccountNumber       = \\u8CEC\\u865F\\uFF1A\nLabel.AccountOptions      = \\u5E33\\u6236\\u9078\\u9805:\nLabel.AccountSeparator    = \\u5E33\\u6236\\u5206\\u9694\\u7B26\\u865F:\nLabel.AccountStatus       = \\u5E33\\u6236\\u72C0\\u614B:\nLabel.AccountType         = \\u5E33\\u6236\\u985E\\u578B:\nLabel.Action              = Action:\nLabel.Amount              = \\u91D1\\u984D/Amount:\nLabel.AnIntRate           = \\u5E74\\u5229\\u7387 (APR):\nLabel.Available           = Available:\nLabel.Balance             = \\u9918\\u984D:\nLabel.BankAccount         = \\u9280\\u884C\\u5E33\\u865F:\nLabel.BankID              = Bank ID:\nLabel.BaseAccount         = \\u57FA\\u672C\\u5E33\\u865F/Base Account:\nLabel.BaseColor           = \\u57FA\\u672C\\u8272\\uFF1A\nLabel.Bottom              = Bottom:\nLabel.By                  = By:\nLabel.CashBalance         = \\u73FE\\u91D1\\u9918\\u984D:\nLabel.Close               = \\u6536\\u76D8\\u4EF7:\nLabel.Color=Color:\nLabel.Commodity           = \\u5546\\u54C1:\nLabel.CompDaysPerYear     = \\u65E5\\u8907\\u5229\\u7387/Compounding Days per Year:\nLabel.CompPerTerm         = \\u671F\\u9593\\u8907\\u5229\\u7387/Compounding Periods per Year:\nLabel.Compare             = Compare:\nLabel.ConfirmPassword     = \\u786E\\u8BA4\\u5BC6\\u7801:\nLabel.ConnTimeout         = \\u8FDE\\u63A5\\u8D85\\u65F6\\u65F6\\u957F:\nLabel.Count               = Count:\nLabel.CreateCurr          = \\u81EA\\u5B9A\\u5E63\\u5225:\nLabel.CssFiles            = CSS\\u6587\\u4EF6\nLabel.CsvFiles            = Csv\\u6587\\u4EF6\nLabel.Curr/Comm           = \\u8CA8\\u5E63 / \\u5546\\u54C1:\nLabel.Currencies          = \\u8CA8\\u5E63:\nLabel.Currency            = \\u5E01\\u79CD:\nLabel.Current             = Current:\nLabel.DatabaseBackend     = \\u6570\\u636E\\u5E93\\u540E\\u53F0:\nLabel.DatabaseName        = \\u6570\\u636E\\u5E93\\u4F4D\\u7F6E:\nLabel.DatabaseServer      = \\u6570\\u636E\\u5E93\\u670D\\u52A1\\u5668:\nLabel.Date                = \\u65E5\\u671F:\nLabel.DateFormat          = \\u65E5\\u671F\\u683C\\u5F0F:\nLabel.DaysPastDue         = \\u9810\\u671F\\u5929\\u6578:\nLabel.DefaultCurrency     = \\u9810\\u8A2D\\u5E63\\u5225:\nLabel.DelaySec            = \\u5EF6\\u9072 (\\u79D2)\nLabel.Description         = \\u63CF\\u8FF0:\nLabel.DestAccount         = \\u76EE\\u7684\\u5E33\\u6236/Destination Account:\nLabel.Difference          = \\u5DEE\\u7570/Difference:\nLabel.Dividend            = \\u5206\\u7D05/\\u5E74\\u606F/Dividend:\nLabel.EndDate             = \\u7D50\\u675F\\u65E5\\u671F/End Date:\nLabel.EndOn               = End On:\nLabel.EndRow              = \\u672B\\u884C:\nLabel.EndingBalance       = \\u6700\\u7D42\\u9918\\u984D/Ending Balance:\nLabel.EquityAccount       = \\u8CC7\\u672C\\u6B0A\\u76CA\\u5E33\\u6236/Equity Account:\nLabel.EscrowPmi           = \\u4FE1\\u8A17/Escrow, PMI, etc:\nLabel.Event               = \\u4E8B\\u4EF6:\nLabel.Every               = Every:\nLabel.ExchangeAmount      = \\u5151\\u6362\\u91D1\\u989D:\nLabel.ExchangeRate        = \\u532F\\u7387:\nLabel.ExchangedAmount     = \\u5151\\u6362\\u6240\\u5F97\\u91D1\\u989D:\nLabel.Fees                = \\u8CBB\\u7528:\nLabel.FeesAccount         = \\u8CBB\\u7528\\u652F\\u51FA\\u5E33\\u6236/Fees Account:\nLabel.FileName            = \\u6A94\\u6848\\u540D\\u7A31:\nLabel.FillAll             = Fill All:\nLabel.FirstPayDate        = \\u9996\\u6B21\\u4ED8\\u6B3E\\u65E5/Date of first payment:\nLabel.FocusColor          = \\u805A\\u7126\\u989C\\u8272\\uFF1A\nLabel.Format              = Format:\nLabel.Frequency           = \\u983B\\u7387:\nLabel.FullNumFormat       = Full Numeric Format:\nLabel.Gains               = \\u83B7\\u5229:\nLabel.HeaderTitle         = Headers / Footers / Title:\nLabel.Height              = \\u9AD8\\u5EA6/Height:\nLabel.High                = \\u9AD8\\u50F9:\nLabel.Host                = \\u4E3B\\u6A5F:\nLabel.Icon=Icon:\nLabel.IEXCloudAttribution = Data provided by IEX Cloud (https://iexcloud.io)\nLabel.IEXCloudSecretKey   = IEX Cloud Secret Key:\nLabel.ISIN                = ISIN:\nLabel.IncomeAccount       = \\u6536\\u5165\\u8D26\\u53F7:\nLabel.InterestAccount     = \\u5229\\u606F\\u5E33\\u6236/Interest Account:\nLabel.LastOccurrence      = \\u6700\\u5F8C\\u767C\\u751F/Last Occurrence:\nLabel.Layout              = \\u7248\\u9762/Layout:\nLabel.Left                = Left:\nLabel.LoanTerm            = \\u8CB8\\u6B3E\\u6642\\u9593/Length of Loan (Months):\nLabel.Low                 = \\u4F4E\\u50F9:\nLabel.MarketValue         = \\u5E02\\u5834\\u50F9\\u683C:\nLabel.MaxBackupCount      = \\u4FDD\\u7559\\u5907\\u4EFD\\u7684\\u6700\\u5927\\u6570\\u91CF:\nLabel.Memo                = \\u5099\\u8A3B:\nLabel.Monospace           = \\u56FA\\u5B9A\\u5B57\\u578B:\nLabel.Name                = \\u540D\\u7A31:\nLabel.NetIncome           = \\u6DE8\\u6536\\u5165:\nLabel.NewPassword         = \\u65B0\\u5BC6\\u7801:\nLabel.NextPayDate         = \\u4E0B\\u6B21\\u4ED8\\u6B3E\\u65E5:\nLabel.Notes               = \\u8A3B\\u8A18:\nLabel.NumTrans            = \\u4EA4\\u6613\\u6B21\\u6578:\nLabel.Number              = \\u865F\\u78BC:\nLabel.OfxFiles            = Ofx\\u6587\\u4EF6\nLabel.OpenStateDate       = \\u521D\\u59CB\\u8D22\\u52A1\\u65E5\\u671F/Opening Statement Date:\nLabel.OpeningBalance      = \\u521D\\u59CB\\u91D1\\u984D/Opening Balance:\nLabel.OrigLoanAmt         = \\u6700\\u521D\\u8CB8\\u6B3E\\u91D1\\u984D/Original Loan Amount:\nLabel.PDFFiles            = PDF Files\nLabel.Password            = \\u5BC6\\u78BC:\nLabel.Path                = \\u6A94\\u6848\\u8DEF\\u5F91:\nLabel.Pattern             = Pattern:\nLabel.PayPerTerm          = \\u6BCF\\u5E74\\u4ED8\\u6B3E\\u91D1\\u984D/Payments per Year:\nLabel.Payee               = \\u6536\\u6B3E\\u4EBA/Payee:\nLabel.Period              = \\u5468\\u671F\\uFF1A\nLabel.Port                = Port:\nLabel.Prefix              = \\u5B57\\u9996/Prefix:\nLabel.Price               = \\u50F9\\u683C:\nLabel.Proportional        = \\u8B8A\\u52D5\\u5B57\\u578B/Proportional:\nLabel.Quantity            = \\u6578\\u91CF/Quantity:\nLabel.QuoteSource         = \\u62A5\\u4EF7\\u6765\\u6E90:\nLabel.ReceivingAccount    = \\u5E94\\u6536\\u6B3E\\u8D26\\u53F7:\nLabel.ReconciledBalance   = \\u5C0D\\u5E33\\u8ABF\\u7BC0\\u9918\\u984D/Reconciled Balance:\nLabel.RemindLater         = \\u7A0D\\u5F8C\\u63D0\\u9192/Remind me again after:\nLabel.RenameBudget        = Rename budget to:\nLabel.RepeatOn            = \\u91CD\\u8907/Repeat on:\nLabel.ReportColumns       = Report Columns:\nLabel.ReportedCurrency    = Reported Currency:\nLabel.Resolution          = \\u7C92\\u5EA6:\nLabel.ReturnOfCapital     = \\u8D44\\u672C\\u56DE\\u62A5:\nLabel.Right               = Right:\nLabel.RoundingMode        = Rounding Mode:\nLabel.Scale               = \\u5C3A\\u5BF8:\nLabel.Securities          = \\u8B49\\u5238/Securities:\nLabel.Security            = \\u8B49\\u5238/Security:\nLabel.ShortNumFormat      = Short Numeric Format:\nLabel.ShowEmptyAccounts   = \\u663E\\u793A\\u7A7A\\u8D26\\u6237\nLabel.SortOrder           = Sort Order:\nLabel.SpreadsheetFiles    = xls\\u6587\\u4EF6\nLabel.StartDate           = \\u958B\\u59CB\\u65E5\\u671F:\nLabel.StartDay            = Start Day:\nLabel.StartMonth          = Start Month:\nLabel.StartPos            = \\u958B\\u59CB\\u4F4D\\u7F6E/Start Position:\nLabel.StartRow            = \\u8D77\\u59CB\\u884C\nLabel.StatementDate       = \\u7D50\\u5E33\\u65E5\\u671F\\uFF1A\nLabel.StorageType         = \\u5B58\\u50A8\\u5F62\\u5F0F:\nLabel.Suffix              = \\u5B57\\u5C3E/Suffix:\nLabel.Symbol              = \\u8A18\\u865F/Symbol:\nLabel.TargetBalance       = \\u76EE\\u6A19\\u9918\\u984D/Target Balance:\nLabel.Top                 = Top:\nLabel.Total               = \\u5408\\u8A08:\nLabel.Transaction         = \\u4EA4\\u6613:\nLabel.TransferFrom        = \\u8F49\\u51FA/Transfer From:\nLabel.TransferTo          = \\u8F49\\u5165/Transfer To:\nLabel.Type                = \\u985E\\u578B/Type:\nLabel.Units               = Units:\nLabel.UserName            = \\u7528\\u6236\\u540D\\u7A31:\nLabel.Value               = \\u4EF7\\u503C:\nLabel.Verify              = \\u5BC6\\u78BC\\u78BA\\u8A8D:\nLabel.VerifyPassword      = \\u786E\\u8BA4\\u5BC6\\u7801:\nLabel.Visible             = \\u986F\\u793A:\nLabel.Volume              = \\u6578\\u91CF:\nLabel.Width               = Width:\nLabel.XMLFiles            = XML\\u6587\\u4EF6\nLabel.Year                = \\u5E74\nLabel.jGnashFiles         = jGnash\\u652F\\u6301\\u7684\\u6587\\u4EF6\n\nMenu.About.Name                       = \\u95DC\\u65BC...\nMenu.About.Tooltip                    = Information about jGnash\nMenu.Account.Name                     = \\u5E33\\u6236\nMenu.AccountRegister.Name             = \\u5E33\\u6236\\u767B\\u9304...\nMenu.AddRemoveCurrency.Name           = \\u65B0\\u589E/\\u79FB\\u9664...\nMenu.BackgroundCurrencyUpdate.Name    = \\u66F4\\u65B0\\u8CA8\\u5E63\nMenu.BackgroundCurrencyUpdate.Tooltip = Updates all exchange rates in the background\nMenu.BackgroundSecurityUpdate.Name    = \\u66F4\\u65B0\\u8B49\\u5238\nMenu.BackgroundSecurityUpdate.Tooltip = Updates all security prices in the background\nMenu.BalanceSheet.Name                = \\u4F59\\u989D\\u8868...\nMenu.BaseColor.Name                   = \\u66F4\\u6539\\u57FA\\u672C\\u989C\\u8272\\u2026\nMenu.BudgetManager.Name               = \\u9884\\u7B97\\u7BA1\\u7406\\u5668\nMenu.ChangeCredentials.Name           = \\u4FEE\\u6539\\u6570\\u636E\\u5E93\\u5BC6\\u7801...\nMenu.ChangeCredentials.Tooltip        = Changes the password of a secure database\nMenu.Charts.Name                      = \\u56FE\\u8868\nMenu.Cleared.Name                     = \\u5DF2\\u7D50\\u6E05\nMenu.Close.Name                       = \\u95DC\\u9589\nMenu.Close.Tooltip                    = Close the active File\nMenu.CloseAllWindows.Name             = \\u5173\\u95ED\\u6240\\u6709\\u7A97\\u53E3\nMenu.ConfigImportFilters.Name         = \\u914D\\u7F6E\\u4EA4\\u6613\\u5BFC\\u5165\\u8FC7\\u6EE4\\u5668...\nMenu.Console.Name                     = Console\\u2026\nMenu.Copy.Name                        = \\u8907\\u88FD\nMenu.Copy.Tooltip                     = Creates a duplicate of the selected item\nMenu.CopyToClipboard.Name             = Copy to Clipboard\nMenu.Currency.Name                    = \\u8CA8\\u5E63\nMenu.CustomStyleSheetApply.Name       = Apply Custom Style Sheet\\u2026\nMenu.CustomStyleSheetRemove.Name      = Remove Custom Style Sheet\nMenu.DefaultCurrency.Name             = \\u9009\\u62E9\\u9ED8\\u8BA4\\u8D27\\u5E01...\nMenu.DefaultCurrency.Tooltip          = \\u66F4\\u6539\\u9ED8\\u8BA4\\u8D27\\u5E01...\nMenu.Delete.Name                      = \\u522A\\u9664\nMenu.Duplicate.Name                   = \\u5236\\u4F5C\\u526F\\u672C\nMenu.Edit.Name                        = \\u7DE8\\u8F2F\nMenu.EditTranNumList.Name             = \\u7F16\\u8F91\\u4EA4\\u6613\\u53F7\\u7801\\u5217\\u8868...\nMenu.Exit.Name                        = \\u7D50\\u675F\nMenu.Exit.Tooltip                     = Exit jGnash\nMenu.Export.Name                      = \\u532F\\u51FA/_Export\nMenu.ExportAccounts.Name              = \\u5BFC\\u51FA\\u8D26\\u6237...\nMenu.File.Archive                     = \\u5099\\u4EFD\\u6A94\\u6848/Archive...\nMenu.File.Name                        = \\u6A94\\u6848\nMenu.Filter.Name                      = \\u7BE9\\u9078/_Filters\nMenu.FontSize.Name                    = \\u66F4\\u6539\\u9ED8\\u8BA4\\u5B57\\u4F53\\u5B57\\u53F7...\nMenu.Help.Name                        = \\u8AAA\\u660E\nMenu.Hide.Name                        = \\u96B1\\u85CF\nMenu.HistoryChart.Name                = \\u5716\\u8868/Historical Chart...\nMenu.HistoryCommodity.Name            = \\u6B77\\u53F2/_History ...\nMenu.HistoryImport.Name               = \\u532F\\u5165\\u6B77\\u53F2/Historical Import...\nMenu.IEBarChart.Name                  = \\u6536\\u5165/\\u652F\\u51FA\\u67F1\\u72B6\\u56FE\nMenu.IEPieChart.Name                  = \\u6536\\u5165/\\u652F\\u51FA \\u5713\\u5F62\\u5716\\u8868 / Income / Expense Pie Chart...\nMenu.Import.Name                      = \\u532F\\u5165/_Import\nMenu.ImportAccounts.Name              = \\u5BFC\\u5165\\u8D26\\u6237...\nMenu.ImportAccounts.Tooltip           = Import a jGnash Accounts File\nMenu.ImportJgnash.Name                = \\u5BFC\\u5165jGnash...\nMenu.ImportJgnash.Tooltip             = Import jGnash 1.11.x files\nMenu.ImportMt940.Name                 = MT940...\nMenu.ImportMt940.Tooltip              = Import SWIFT MT940 files\nMenu.ImportOfx.Name                   = OFX / QFX\\u2026\nMenu.ImportOfx.Tooltip                = Import OFX and QFX files\nMenu.ImportQif.Name                   = _QIF...\nMenu.Jump.Name                        = \\u79FB\\u81F3\nMenu.ListOfAccounts.Name              = \\u5E33\\u6236\\u540D\\u55AE\\u2026\nMenu.Locale.Name                      = \\u8A2D\\u5B9A\\u8A9E\\u8A00...\nMenu.LookAndFeel.Name                 = \\u4F7F\\u7528\\u8005\\u4ECB\\u9762/Look and Feel\nMenu.MarkAs.Name                      = \\u6807\\u8BB0\\u4E3A\nMenu.Modify.Name                      = \\u7DE8\\u8F2F\nMenu.ModifyCommodity.Name             = \\u65B0\\u589E/\\u7DE8\\u8F2F\\u5546\\u54C1 ...\nMenu.ModifyCurrency.Name              = \\u7DE8\\u8F2F\\u5E01\\u79CD...\nMenu.ModifyExchangeRates.Name         = \\u7F16\\u8F91\\u6C47\\u7387...\nMenu.MonthBalance.Name                = \\u6708\\u9918\\u984D...\nMenu.MonthBalanceCompare.Name         = \\u6708\\u4F59\\u989D\\u6BD4\\u5BF9\nMenu.MonthEndBalance.Name             = \\u6708\\u7D42\\u9918\\u984D...\nMenu.MonthEndBalanceCSV.Name          = \\u6708\\u7D42\\u9918\\u984D (CSV)...\nMenu.NetWorth.Name                    = \\u8D44\\u672C\\u51C0\\u503C...\nMenu.New.Name                         = \\u65B0\\u589E\nMenu.New.Tooltip                      = Create a new file\nMenu.NewReminder.Name                 = \\u521B\\u5EFA\\u65B0\\u63D0\\u9192\nMenu.Open.Name                        = \\u958B\\u555F\nMenu.Open.Tooltip                     = Open the specified file\nMenu.Option.Name                      = \\u9078\\u9805...\nMenu.Option.Tooltip                   = \\u8B8A\\u66F4\\u7A0B\\u5F0F\\u9078\\u9805\nMenu.PackDatabase.Name                = \\u6253\\u5305\\u6570\\u636E\\u5E93...\nMenu.PayeePieChart.Name               = \\u6309\\u6536\\u6B3E\\u4EBA\\u5206\\u7C7B\\u7684\\u6536\\u5165/\\u652F\\u51FA\\u997C\\u56FE\nMenu.PeriodicAccountBalance.Name      = \\u5B9A\\u671F\\u5E33\\u6236\\u9918\\u984D...\nMenu.Portfolio.Name                   = \\u6295\\u8D44\\u7EC4\\u5408...\nMenu.ProfitLoss.Name                  = \\u640D\\u76CA\\u8868/Profit and Loss...\nMenu.ProfitLossTXT.Name               = \\u640D\\u76CA\\u8868/Profit and Loss (Text)...\nMenu.Reconcile.Name                   = \\u5C0D\\u5E33\\u8ABF\\u7BC0/Reconcile\nMenu.Reconciled.Name                  = \\u5DF2\\u5BF9\\u8D26\nMenu.RecurringList.Name               = \\u91CD\\u8907\\u578B\\u4EA4\\u6613/Recurring Transactions...\nMenu.Register.Name                    = \\u767B\\u9304...\nMenu.Reports.Name                     = \\u5831\\u8868\nMenu.RunJavaScript.Name               = Run JavaScript...\nMenu.RunJavaScript.Tooltip            = Run JavaScript\nMenu.Save.Name                        = \\u5132\\u5B58/_Save\nMenu.Save.Tooltip                     = Save the current file\nMenu.SaveAs.Name                      = \\u53E6\\u5B58\\u65B0\\u6A94...\nMenu.SaveAs.Tooltip                   = Save the current file under a new name\nMenu.Securities.Name                  = \\u8BC1\\u5238\nMenu.SetPassword.Name                 = \\u8BBE\\u7F6E\\u5BC6\\u7801...\nMenu.Show.Name                        = \\u540E\\u53F0\\u66F4\\u65B0\\u986F\\u793A\\u540D\\u7A31\nMenu.ShutdownServer.Name              = \\u5173\\u95ED\\u670D\\u52A1\\u5668\nMenu.ShutdownServer.Tooltip           = Shutdown the server and prevent further communication\nMenu.TagManager.Name=Tag Manager\\u2026\nMenu.Themes.Name                      = \\u4F48\\u666F\\u4E3B\\u984C\nMenu.Tools.Name                       = \\u5DE5\\u5177\nMenu.TransactionTagPieChart.Name=Transaction Tag Pie Chart\\u2026\nMenu.Unreconciled.Name                = \\u672A\\u5BF9\\u8D26\nMenu.View.Name                        = \\u986F\\u793A\nMenu.Window.Name                      = _Window\n\nMessage.AcceptLicense                = I have read, understand, and accept the terms of the licenses.\nMessage.AccountAdd                   = \\u5E33\\u865F\\u65B0\\u589E\nMessage.AccountCode                  = \\u5E33\\u865F\\u4EE3\\u78BC\\u975E\\u552F\\u4E00: \\u4EE3\\u78BC\\u672A\\u4FEE\\u6539\nMessage.AccountLocked                = \\u5E33\\u6236\\u88AB\\u9396\\u5B9A\nMessage.AccountModify                = \\u5E33\\u865F\\u4FEE\\u6539\nMessage.AccountMoveFailed            = Could not move the account\nMessage.AccountRemove                = \\u5E33\\u865F\\u79FB\\u9664\nMessage.AntiAlias                    = Enabling text antialiasing!\nMessage.AutoSaveOff                  = \\u81EA\\u52D5\\u5B58\\u6A94\\u95DC\\u9589\nMessage.AutoSaveOn                   = \\u81EA\\u52D5\\u5B58\\u6A94\\u958B\\u555F\nMessage.CSVFile                      = \\u9017\\u53F7\\u5206\\u9694\\u6587\\u4EF6(*.csv)\nMessage.CheckRecurring               = \\u5FAA\\u74B0\\u4E8B\\u4EF6\\u6AA2\\u67E5\\u4E2D\nMessage.ClosingFile                  = \\u6B63\\u5728\\u5173\\u95ED\\u6587\\u4EF6\nMessage.CollectingReportData         = \\u6B63\\u5728\\u6536\\u96C6\\u62A5\\u544A\\u6570\\u636E\nMessage.CompilingReport              = \\u6B63\\u5728\\u751F\\u6210\\u62A5\\u544A\nMessage.Confirm.ExecuteReminder      = Execute the Reminder now?\nMessage.ConfirmBudgetDelete          = \\u786E\\u8BA4\\u5220\\u9664\\u9009\\u4E2D\\u7684\\u6B64\\u6761\\u9884\\u7B97?\nMessage.ConfirmMultipleBudgetDelete  = \\u786E\\u8BA4\\u5220\\u9664\\u9009\\u4E2D\\u7684\\u591A\\u6761\\u9884\\u7B97?\nMessage.ConfirmMultipleTransDelete   = \\u522A\\u9664\\u9078\\u5B9A\\u4EA4\\u6613?\nMessage.ConfirmReminderDelete        = \\u522A\\u9664\\u63D0\\u9192?\nMessage.ConfirmSecurityHistoryDelete = Delete the selected security history?\\n\\nHistory removal will occur in the background and could take awhile.\nMessage.ConfirmTransDelete           = \\u522A\\u9664\\u4EA4\\u6613?\nMessage.CredentialChange             = Credentials change was successful\nMessage.CurrChange                   = \\u6539\\u8B8A\\u9810\\u8A2D\\u5E63\\u5225\nMessage.DownloadingX                 = \\u6B63\\u5728\\u4E0B\\u8F7D {0}\nMessage.EngineStart                  = Engine started\nMessage.EnterNetworkAuth             = Enter Network Authentication\nMessage.Error.AccountCreate          = \\u7121\\u6CD5\\u65B0\\u589E\\u5E33\\u865F\nMessage.Error.AccountRemove          = \\u7121\\u6CD5\\u79FB\\u9664\\u5E33\\u6236\nMessage.Error.AccountUpdate          = An error occurred updating the account\nMessage.Error.AddCommodity           = \\u7121\\u6CD5\\u65B0\\u589E\\u5546\\u54C1\nMessage.Error.AddCurrency            = \\u7121\\u6CD5\\u65B0\\u589E\\u8CA8\\u5E63\nMessage.Error.AmortizationSave       = Failed to save the amortization configuration\nMessage.Error.BudgetDuplicate        = Failed to duplicate the budget\nMessage.Error.BudgetRemove           = Failed to remove the budget\nMessage.Error.CreateBasicAccounts    = \\u5148\\u65B0\\u589E\\u57FA\\u672C\\u5E33\\u6236/Create a basic account set first\nMessage.Error.CredentialChange       = Credentials change failed\nMessage.Error.CreditDebit.Equal      = Credit and Debit accounts may be be equal\nMessage.Error.CurrencyUpdate         = Unable to update currency {0}\nMessage.Error.DataSupplierToken      = The Data supplier token for {0} has not been specified\nMessage.Error.DeleteAttachment       = Unable to delete the attachment {0}\nMessage.Error.DeleteExistingFile     = Unable to delete the existing file {0}\nMessage.Error.Duplicate              = Duplicates are not permitted\nMessage.Error.EmptyKey               = Attribute key may not be empty or null\nMessage.Error.FileNotFound           = \\u6A94\\u6848\\u4E0D\\u5B58\\u5728\nMessage.Error.HistRemoval            = Unable to remove the history node {0,date,MM/dd/yyyy} from {1}\nMessage.Error.IOError                = IO Error\nMessage.Error.InvalidAccountGroup    = Invalid account group\nMessage.Error.InvalidTransactionTag  = Invalid transaction tag\nMessage.Error.InvalidTransactionType = Invalid transaction type\nMessage.Error.InvalidUserPass        = Invalid password or tried to open the wrong file type\nMessage.Error.License                = The license was not accepted\nMessage.Error.LoadingFile            = \\u7121\\u6CD5\\u8F09\\u5165\\u6A94\\u6848\nMessage.Error.LogFileHandler         = Could not install log file handler\nMessage.Error.MissingAttachment      = The attachment \"{0}\" could not be found\nMessage.Error.ModifyCommodity        = \\u7121\\u6CD5\\u8B8A\\u66F4\\u5546\\u54C1\nMessage.Error.ModifyCurrency         = \\u7121\\u6CD5\\u8B8A\\u66F4\\u8CA8\\u5E63\nMessage.Error.MoveAccount            = Unable to move the account\nMessage.Error.NewBudget              = Failed to create the new budget\nMessage.Error.ParseTransactions      = \\u7121\\u6CD5\\u5206\\u6790\\u4EA4\\u6613\\u9805\\u76EE/Did not parse any transactions\nMessage.Error.PasswordMatch          = Passwords do not match\nMessage.Error.ReminderAdd            = Failed to add the reminder\nMessage.Error.ReminderUpdate         = Failed to update the reminder\nMessage.Error.RemoveTempFile         = Unable to remove temporary file\nMessage.Error.SecurityAccountRemove  = Failed to remove security {0} from account {1}\nMessage.Error.SecurityAccountUpdate  = Unable to update account securities\nMessage.Error.SecurityAdd            = Failed to add security {0}\nMessage.Error.SecurityUpdate         = Unable to update security {0}\nMessage.Error.ServerConnection       = The connection to the server failed\nMessage.Error.TranAddFail            = An internal error occurred when adding a transaction\nMessage.Error.TransferAttachment     = The attachment \"{0}\" could not be transferred\nMessage.Error.UnsupportedFileType    = Unsupported supported file type\nMessage.FileClosed                   = \\u6A94\\u6848\\u95DC\\u9589\nMessage.FileIsLocked                 = The file is locked by another application\nMessage.FileLoadComplete             = \\u6587\\u4EF6\\u8F7D\\u5165\\u5B8C\\u6210\nMessage.FileNotValid                 = The selected file is not valid\nMessage.FileSaveComplete             = File save complete\nMessage.ImportWait                   = Please wait, import may take awhile\nMessage.Info.LongUpgrade             = Your file will be upgraded to the latest format. This may take awhile to complete.\nMessage.Info.RestartToApply          = Restart to apply changes\nMessage.Info.Upgrade                 = Your file was upgraded to the latest format.\\nThe original file was saved as \"{0}\".\nMessage.JVM11                        = jGnash requires Java 11 or newer\nMessage.LoadReportFail               = \\u7121\\u6CD5\\u8F09\\u5165\\u5831\\u8868\\u5B9A\\u7FA9\nMessage.LoadingFile                  = \\u6B63\\u5728\\u8F7D\\u5165\\u6587\\u4EF6\nMessage.LocaleChange                 = \\u8B8A\\u66F4\\u8A9E\\u8A00\\u81F3:\nMessage.NewVersion                   = A newer version of jGnash is available for download.\nMessage.NoRepeat                     = \\u7121\\u6CD5\\u91CD\\u8986/Does not repeat\nMessage.OpenJfxDownload              = The operating system specific OpenJFX libraries are being downloaded.\\n\\njGnash will need to be restarted after the download is complete.\nMessage.OverwriteDB                  = The existing database will be overwritten\nMessage.PackingFile                  = Packing database file\nMessage.PackingFileComplete          = File pack is Complete\nMessage.ParseReportFail              = \\u7121\\u6CD5\\u5206\\u6790\\u5831\\u8868\\u5B9A\\u7FA9/Failed to parse the report definition\nMessage.PleaseWait                   = Please Wait\nMessage.PrefFail                     = Did not find old preferences\nMessage.PrepShutdown                 = \\u6E96\\u5099\\u7D50\\u675F\\u4E2D\nMessage.ProcessingReportData         = Processing report data\nMessage.Proxy                        = Setting http proxy:\nMessage.ReduceFont                   = Try reducing your font size.\\nPlease see the Report help for details.\nMessage.RemovingSecurityHistory      = \"Removing security price history dated {0} from {1}\nMessage.ReportModLoaded              = \\u5831\\u8868\\u6A21\\u7D44\\u6210\\u529F\\u8F09\\u5165\nMessage.ReportWait                   = \\u5831\\u8868\\u6A21\\u7D44\\u8F09\\u5165\\u4E2D \\u8ACB\\u7A0D\\u5019\nMessage.RestartLocale                = \\u91CD\\u65B0\\u555F\\u52D5\\u8B93\\u8A9E\\u8A00\\u8B8A\\u66F4\\u751F\\u6548\nMessage.SavingFile                   = Saving file\\u2026\nMessage.SearchWait                   = Searching, Please Wait\nMessage.Shutdown                     = Shutdown\nMessage.StartEndDate                 = \\u8F38\\u5165\\u958B\\u59CB\\u53CA\\u7D50\\u675F\\u65E5\\u671F\nMessage.StoreBackup                  = \\u5132\\u5B58\\u5099\\u5206\\u6A94\\u6848\\u81F3:\nMessage.StoreComplete                = \\u6A94\\u6848\\u5132\\u5B58\\u5B8C\\u7562\nMessage.StoreWait                    = \\u7B49\\u5F85\\u5B58\\u6A94\\u5B8C\\u7562\nMessage.TXTFile                      = \\u6587\\u5B57\\u6A94\\u6848 (*.txt)\nMessage.TransactionAccountLocked     = \\u4EA4\\u6613\\u65B0\\u589E\\u5931\\u6557, \\u5E33\\u865F\\u4E0D\\u53EF\\u4FEE\\u6539\nMessage.TransactionAdd               = \\u4EA4\\u6613\\u65B0\\u589E\\u5B8C\\u6210\nMessage.TransactionModifyLocked      = The transaction cannot be modified.\\nThe destination account is locked against modification.\nMessage.TransactionRemove            = \\u4EA4\\u6613\\u5DF2\\u522A\\u9664\nMessage.TransactionRemoveLocked      = \\u4EA4\\u6613\\u522A\\u9664\\u5931\\u6557, \\u5E33\\u865F\\u4E0D\\u53EF\\u4FEE\\u6539\nMessage.UninstallBad                 = Could not uninstall successfully\nMessage.UninstallGood                = Uninstall successful!\nMessage.UpdatedPrice                 = Updated the price for {0}\nMessage.UpdatedPriceDate             = Updated the price for {0}, {1}\nMessage.UpdatedSecurityEvent         = Updated a security event for {0}\nMessage.Version                      = You are using version\nMessage.Warn.CommodityInUse          = \\u5546\\u54C1\\u76EE\\u524D\\u4F7F\\u7528\\u4E2D\nMessage.Warn.ConfigAmortization      = Please configure amortization\nMessage.Warn.CurrencyInUse           = \\u8CA8\\u5E63\\u76EE\\u524D\\u4F7F\\u7528\\u4E2D\nMessage.Warn.FailedTransInfoRemoval  = Failed to remove old transaction information\nMessage.Warn.MoveFile                = The file \"{0}\" will be moved \\nto the the managed location \"{1}\".\nMessage.Warn.SameFile                = The file \"{0}\" already exists \\nin the the managed location \"{1}\".\\n\\nThe file will not be moved.\nMessage.Warn.WindowWidth             = Window is too small\\n\\nIncrease width or reduce font size.\n\nName.BankAccounts    = \\u9280\\u884C\\u5E33\\u865F\nName.ExpenseAccounts = \\u652F\\u51FA\\u5E33\\u865F\nName.IncomeAccounts  = \\u6536\\u5165\\u5E33\\u865F\nName.Root            = Root\n\nPattern.Date           = {0,date,long}\nPattern.DateRange      = \\u4ECE {0,date,long} \\u81F3 {1,date,long}\nPattern.DateRangeShort = {0,date,MM/dd/yy} - {1,date,MM/dd/yy}\nPattern.MonthOfYear    = {0,date,MM/yyyy}\nPattern.NumericDate    = {0,date,MM/dd/yyyy}\nPattern.Pages          = \\u7B2C {0} \\u9875/\\u5171 {1} \\u9875\nPattern.QuarterOfYear  = \\u7B2C {0,number,#} \\u5B63\\u5EA6/\\u5171 {1,number,#} \\u5B63\\u5EA6\nPattern.WeekOfYear     = \\u7B2C {0,number,00} \\u5468/\\u5171 {1,number,#} \\u5468\n\nPeriod.10Min     = 10 \\u5206\\u9418\nPeriod.15Min     = 15 \\u5206\\u9418\nPeriod.1Day      = 1 \\u65E5\nPeriod.1Hr       = 1 \\u5C0F\\u6642\nPeriod.2Hr       = 2 \\u5C0F\\u6642\nPeriod.30Min     = 30 \\u5206\\u9418\nPeriod.5Min      = 5 \\u5206\\u9418\nPeriod.8Hr       = 8 \\u5C0F\\u6642\nPeriod.BiWeekly  = \\u53CC\\u5468\nPeriod.Daily     = \\u6BCF\\u65E5\nPeriod.Monthly   = \\u6BCF\\u6708\nPeriod.NextStart = \\u4E0B\\u6B21\\u555F\\u52D5 jGnash \\u6642\nPeriod.None      = \\u7121\nPeriod.OnlyOnce  = \\u55AE\\u6B21\nPeriod.Quarterly = \\u6BCF\\u5B63\\u5EA6\nPeriod.Weekly    = \\u6BCF\\u9031\nPeriod.Yearly    = \\u6BCF\\u5E74\n\nQuestion.DeleteAttachment = \\u6B64\\u9879\\u4EA4\\u6613\\u5305\\u542B\\u4E00\\u4E2A\\u9644\\u4EF6.\\n\\n\\u662F\\u5426\\u540C\\u65F6\\u5220\\u9664\\u8BE5\\u9644\\u4EF6?\n\nQuoteSource.None     = None\nQuoteSource.Yahoo    = Yahoo!\nQuoteSource.YahooAus = Yahoo! Australia\nQuoteSource.YahooUK  = Yahoo! UK and Ireland\n\nRoundingMode.Ceiling.Description  = Round towards positive infinity\nRoundingMode.Ceiling.Name         = Ceiling\nRoundingMode.Down.Description     = Round towards zero\nRoundingMode.Down.Name            = Down\nRoundingMode.Floor.Description    = Round towards negative infinity\nRoundingMode.Floor.Name           = Floor\nRoundingMode.HalfDown.Description = Round towards nearest neighbor or down if equal distance\nRoundingMode.HalfDown.Name        = Half Down\nRoundingMode.HalfEven.Description = Round towards nearest neighbor or towards even if equal distance\nRoundingMode.HalfEven.Name        = Half Even\nRoundingMode.HalfUp.Description   = Round towards nearest neighbor or up if equal distance\nRoundingMode.HalfUp.Name          = Half Up\nRoundingMode.Up.Description       = Round away from zero\nRoundingMode.Up.Name              = Up\n\nSecurityEvent.Dividend = \\u80A1\\u7968\\u5206\\u7EA2\nSecurityEvent.Price    = \\u80A1\\u7968\\u5B9A\\u4EF7\nSecurityEvent.Split    = \\u80A1\\u7968\\u62C6\\u5206\n\nSequence.EveryFifthRow  = Every Fifth Row\nSequence.EveryForthRow  = Every Fourth Row\nSequence.EveryOtherRow  = Every Other Row\nSequence.EveryRow       = Every Row\nSequence.EverySecondRow = Every Second Row\nSequence.EveryThirdRow  = Every Third Row\n\nSortOrder.AccountBalanceDesc = \\u6309\\u8D26\\u6237\\u4F59\\u989D\nSortOrder.AccountName        = \\u6309\\u8D26\\u6237\\u540D\\u79F0\n\nState.Cleared       = C\nState.NotReconciled = \\u200B\\u200BN\nState.Reconciled    = R\n\nTab.About           = \\u95DC\\u65BC\nTab.Accounts        = \\u8D26\\u6237\nTab.Adjust          = \\u8ABF\\u6574\nTab.AppLicense      = \\u8BB8\\u53EF\\u534F\\u8BAE\nTab.Budgeting       = \\u9884\\u7B97\nTab.Charge          = \\u8CBB\\u7528\nTab.Credit          = \\u8D37\\u65B9\nTab.Credits         = \\u8363\\u8000\\u5C5E\\u4E8E\nTab.DataEngine      = Data Engine\nTab.DataProviders   = Data Providers\nTab.Day             = \\u65E5\nTab.Debit           = \\u501F\\u65B9\nTab.Formats         = Formats\nTab.GPLLicense      = GPL License\nTab.General         = \\u57FA\\u672C\nTab.LGPLLicense     = LGPL License\nTab.License         = License\nTab.Month           = \\u6708\nTab.Network         = \\u7F51\\u7EDC\nTab.None            = None\nTab.Payment         = \\u4ED8\\u6B3E\nTab.Register        = \\u767B\\u9304\nTab.Reminders       = \\u63D0\\u9192\nTab.Report          = \\u5831\\u8868\nTab.StartupShutdown = Startup / Shutdown\nTab.SysInfo         = \\u7CFB\\u7D71\\u8CC7\\u8A0A\nTab.Transfer        = \\u8F49\\u5E33\nTab.Week            = \\u9031\nTab.Year            = \\u5E74\n\nTag.Bank                   = \\u94F6\\u884C\nTag.Dividend               = \\u7EA2\\u5229\nTag.FeesOffset             = \\u8D39\\u7528\\u62B5\\u6263\nTag.GainLoss               = \\u635F\\u76CA\nTag.GainsOffset            = \\u6536\\u5165\\u62B5\\u6263\nTag.Investment             = \\u6295\\u8D44\\u8D39\\u7528\nTag.InvestmentCashTransfer = \\u6295\\u8D44\\u73B0\\u91D1\\u8F6C\\u6237\nTag.InvestmentFee          = \\u6295\\u8D44\\u8D39\\u7528\nTag.LTCG                   = \\u957F\\u671F\\u8D44\\u672C\\u6536\\u76CA\\u5206\\u914D\nTag.MTCG                   = \\u4E2D\\u671F\\u8D44\\u672C\\u6536\\u76CA\\u5206\\u914D\nTag.Misc                   = \\u5176\\u4ED6\nTag.NonTaxableInterest     = \\u975E\\u5E94\\u7A0E\\u5229\\u606F\nTag.STCG                   = \\u77ED\\u671F\\u8D44\\u672C\\u6536\\u76CA\\u5206\\u914D\nTag.TaxableInterest        = \\u5E94\\u7A0E\\u5229\\u606F\nTag.Vat                    = \\u589E\\u503C\\u7A0E\n\nTitle.About                      = \\u95DC\\u65BC\nTitle.AccountBalance             = \\u5E33\\u6236\\u9918\\u984D/Account Balance\nTitle.AccountFilter              = \\u5E33\\u6236\\u7BE9\\u9078/Account Filters\nTitle.AccountGroups              = \\u8D26\\u6237\\u7EC4\nTitle.AccountInfo                = \\u5E33\\u6236\\u8CC7\\u8A0A/Account Information\nTitle.AccountRegister            = \\u8D26\\u6237\\u767B\\u8BB0\nTitle.AccountSecurities          = \\u5E33\\u6236\\u8B49\\u5238/Account Securities\nTitle.AddRemCurr                 = \\u65B0\\u589E/\\u79FB\\u9664\\u8CA8\\u5E63\nTitle.AmortizationSetup          = \\u6524\\u9084\\u8A2D\\u5B9A/Amortization Setup\nTitle.Archive                    = \\u6B77\\u53F2\\u5B58\\u6A94Archive\nTitle.AutoComplete               = \\u81EA\\u52A8\\u5B8C\\u6210\nTitle.AutoSave                   = \\u81EA\\u52D5\\u5B58\\u6A94\nTitle.Available                  = Available\nTitle.BackgroundUpdate           = \\u540E\\u53F0\\u66F4\\u65B0\nTitle.BackingStore               = Backing Store\nTitle.BalanceSheet               = \\u4F59\\u989D\\u8868\nTitle.BaseColor                  = \\u66F4\\u6539\\u57FA\\u672C\\u989C\\u8272\nTitle.BudgetGoal                 = \\u9884\\u7B97\\u76EE\\u6807\nTitle.BudgetManager              = \\u9884\\u7B97\\u7BA1\\u7406\\u5668\nTitle.BudgetProperties           = \\u9884\\u7B97\\u5C5E\\u6027\nTitle.ButtonOrder                = \\u6309\\u94AE\\u987A\\u5E8F\nTitle.ChangePassword             = \\u66F4\\u6539\\u5BC6\\u7801\nTitle.CheckDesign                = \\u652F\\u7968/\\u5E33\\u55AE\\u8A2D\\u8A08/Check Designer\nTitle.ChooseAccounts             = \\u9009\\u62E9\\u8981\\u521B\\u5EFA\\u7684\\u8D26\\u6237\nTitle.ColVis                     = \\u6B04\\u4F4D\\u986F\\u793A\nTitle.Colors                     = \\u984F\\u8272\nTitle.CommoditiesSecurities      = \\u5546\\u54C1/\\u8B49\\u5238 Securities\nTitle.ConfigTransImportFilters   = Configure Transaction Import Filters\nTitle.Confirm                    = \\u78BA\\u8A8D\nTitle.ConnectServer              = \\u8FDE\\u63A5\\u5230\\u670D\\u52A1\\u5668\nTitle.Connection                 = \\u8FDE\\u63A5\\u5C5E\\u6027\nTitle.Console                    = Console\nTitle.CreateModifyCommodities    = \\u65B0\\u589E/\\u7DE8\\u8F2F\\u6709\\u4EF7\\u8BC1\\u5238\nTitle.Credits                    = \\u8CB8\\u65B9\nTitle.Currencies                 = \\u8CA8\\u5E63\nTitle.Current                    = Current\nTitle.CutOffDate                 = \\u9078\\u64C7\\u622A\\u6B62\\u65E5\\u671F/Select Cut Off Date\nTitle.DatabaseCfg                = \\u6570\\u636E\\u5E93\\u914D\\u7F6E\nTitle.DateFormats                = Date Formats\nTitle.Debits                     = \\u501F\\u65B9\nTitle.DefDefCurr                 = \\u9810\\u8A2D\\u8CA8\\u5E63\nTitle.DefTranNum                 = Default Transaction Numbers\nTitle.DefaultBehavior            = \\u9ED8\\u8BA4\\u884C\\u4E3A\nTitle.Defaults                   = Defaults\nTitle.DeleteAttachment           = \\u5220\\u9664\\u9644\\u4EF6\nTitle.Display                    = \\u986F\\u793A\nTitle.DuplicateTransaction       = \\u91CD\\u8907\\u4EA4\\u6613/Duplicate Transaction\nTitle.DuplicateTransactionsFound = \\u767C\\u751F\\u91CD\\u8907\\u4EA4\\u6613/Duplicate Transactions Found\nTitle.EditExchangeRates          = Edit Exchange Rates\nTitle.EndMonthBalance            = \\u6708\\u7D42\\u5E33\\u6236\\u9918\\u984D/End-Of-Month Account Balance\nTitle.EnterPassword              = \\u8F38\\u5165\\u5BC6\\u78BC\nTitle.Entry                      = \\u4EA4\\u6613\\u9805\\u76EE\nTitle.Error                      = \\u932F\\u8AA4\nTitle.EventHistory               = \\u4E8B\\u4EF6\\u5386\\u53F2\nTitle.ExchangeRate               = \\u532F\\u7387\nTitle.FileImport                 = Choose File To Import\nTitle.FileLoginCredentials       = File / Login Credentials\nTitle.Filters                    = \\u904E\\u6FFE\\u5668\nTitle.FontSize                   = Change Default Font Size\nTitle.Fonts                      = \\u9810\\u8A2D\\u5B57\\u578B\nTitle.Frequency                  = \\u983B\\u7387\nTitle.HTTPProxy                  = HTTP Proxy\nTitle.Help                       = jGnash Help\nTitle.HistoryImport              = \\u532F\\u5165\\u6B77\\u53F2\\u8CC7\\u6599\nTitle.ImageFiles                 = Image Files\nTitle.ImpPartQif                 = \\u532F\\u5165\\u90E8\\u5206 QIF \\u6A94\nTitle.ImpSum                     = \\u532F\\u5165 Summary\nTitle.ImportOFX                  = Import an OFX file\nTitle.ImportTransactions         = Import transactions from a file\nTitle.IncomeExpenseBarChart      = Income and Expense Bar Chart\nTitle.IncomeExpenseChart         = Income and Expense Pie Chart\nTitle.Information                = \\u4FE1\\u606F\nTitle.InvFees                    = Investment Fees\nTitle.InvGainsLoss               = Investment Gains / Loss\nTitle.ListOfAccounts             = List of Accounts\nTitle.Margins                    = Margins\nTitle.ModImportTrans             = Modify Transactions\nTitle.ModOFXTrans                = Modify OFX Transactions\nTitle.ModQIFTrans                = \\u7DE8\\u8F2F\nTitle.ModifyAccount              = \\u4FEE\\u6539\\u8D26\\u6237\nTitle.ModifyCurrencies           = \\u7DE8\\u8F2F\\u8CA8\\u5E63\nTitle.ModifyReminder             = \\u4FEE\\u6539\\u63D0\\u9192\nTitle.ModifySecHistory           = \\u7DE8\\u8F2F\\u8B49\\u5238\\u6B77\\u53F2\nTitle.ModifyTransaction          = \\u4FEE\\u6539\\u4EA4\\u6613\nTitle.MoveFile                   = Move File\nTitle.NewAccount                 = \\u65B0\\u8D26\\u6237\nTitle.NewBudget                  = \\u65B0\\u9884\\u7B97\nTitle.NewFile                    = \\u5EFA\\u7ACB\\u6A94\\u6848\nTitle.NewPassword                = \\u65B0\\u5BC6\\u7801\nTitle.NewReminder                = \\u65B0\\u63D0\\u9192\nTitle.NewTrans                   = \\u65B0\\u589E\\u4EA4\\u6613\nTitle.Notes                      = \\u5099\\u8A3B\nTitle.NumericFormats             = Numeric Formats\nTitle.Open                       = \\u958B\\u555F\nTitle.Options                    = \\u9078\\u9805\nTitle.Orientation                = Orientation\nTitle.PackDatabase               = Pack Database\nTitle.PageSetup                  = \\u7248\\u9762\\u8A2D\\u5B9A\nTitle.ParentAccount              = \\u6B78\\u5C6C\\u5E33\\u6236\nTitle.PercentDist                = \\u5206\\u914D\\u6BD4\\u4F8B\nTitle.PercentExpense             = \\u6D88\\u8CBB\\u6BD4\\u4F8B\nTitle.PercentIncome              = \\u6536\\u5165\\u6BD4\\u4F8B\nTitle.PleaseWait                 = \\u8ACB\\u7A0D\\u7B49\nTitle.PortfolioReport            = \\u8D44\\u4EA7\\u7EC4\\u5408\\u62A5\\u544A\nTitle.PriceHistory               = \\u4EF7\\u683C\\u5386\\u53F2\nTitle.ProfitLoss                 = \\u8CC7\\u7522\\u8CA0\\u50B5\\u8868\nTitle.ReconcileSettings          = \\u5C0D\\u5E33\\u8ABF\\u7BC0\\u8A2D\\u5B9A\nTitle.Reminder                   = \\u63D0\\u9192\nTitle.Reminders                  = \\u63D0\\u9192\nTitle.RenameBudget               = \\u9884\\u7B97\\u6539\\u540D\nTitle.ReportOptions              = Report Options\nTitle.ReportSize                 = Report Size\nTitle.RetainedEarnings           = \\u7559\\u5B58\\u6536\\u76CA\nTitle.ReverseAccountBalances     = \\u7EA2\\u5B57\\u663E\\u793A\\u8D26\\u6237\\u4F59\\u989D\nTitle.Rounding                   = Rounding\nTitle.SaveAs                     = \\u53E6\\u5B58\\u4E3A\nTitle.SaveFile                   = \\u5132\\u5B58\\u6A94\\u6848\nTitle.SecurityHistory            = \\u8B49\\u5238\\u6B77\\u53F2\nTitle.SelAccount                 = \\u9078\\u64C7\\u5E33\\u6236\nTitle.SelAvailCurr               = \\u9078\\u64C7\\u53EF\\u7528\\u5E63\\u5225\nTitle.SelDate                    = \\u9078\\u64C7\\u65E5\\u671F\nTitle.SelDefCurr                 = \\u9078\\u64C7\\u9810\\u8A2D\\u8CA8\\u5E63\nTitle.SelDefLocale               = \\u9078\\u64C7\\u9810\\u8A2D\\u8A9E\\u8A00\nTitle.SelDestAccount             = \\u9078\\u64C7\\u76EE\\u6A19\\u5E33\\u6236\nTitle.SelTransTags=Select Transaction Tags\nTitle.SelEquAccount              = \\u9078\\u64C7\\u8CC7\\u672C\\u6B0A\\u76CA\\u5E33\\u6236\nTitle.SelFile                    = \\u9078\\u64C7\\u6A94\\u6848\nTitle.SelQifDateFormat           = Select QIF date format\nTitle.SelectColor                = \\u9078\\u64C7\\u984F\\u8272\nTitle.Selected                   = \\u9078\\u64C7\nTitle.Shutdown                   = \\u5173\\u95ED\nTitle.SmartFill                  = Smart Fill\nTitle.SpitTran                   = \\u62C6\\u5206\\u4EA4\\u6613\nTitle.Startup                    = \\u542F\\u52A8\nTitle.Steps                      = \\u6B65\\u9A5F\nTitle.Success                    = \\u6210\\u529F\nTitle.Summary                    = \\u6982\\u8981\nTitle.TagManager=Tag Manager\nTitle.Terms                      = \\u8BCD\\u6C47\nTitle.Transaction                = \\u4EA4\\u6613\nTitle.TransactionImport          = \\u4EA4\\u6613\\u5BFC\\u5165\nTitle.TransactionList            = \\u4EA4\\u6613\\u5217\\u8868\nTitle.TransactionSetup           = \\u4EA4\\u6613\\u8A2D\\u5B9A\nTitle.TransactionTagPieChart=Transaction Tag Pie Chart\nTitle.UncaughtException          = Uncaught Exception\nTitle.ViewImage                  = \\u67E5\\u770B\\u56FE\\u7247\nTitle.Visible                    = \\u986F\\u793A\nTitle.VisibleAccountTypes        = \\u53EF\\u89C1\\u8D26\\u6237\\u7C7B\\u578B\nTitle.Warning                    = \\u8B66\\u544A\n\nToolTip.AccountList                          = \\u5E33\\u6236\\u5217\\u8868\nToolTip.AccountRegister                      = \\u5E33\\u6236\\u767B\\u9304\nToolTip.AddAttachment                        = Add attachment\nToolTip.BudgetMgr                            = Create, delete, and modify budgets\nToolTip.Budgeting                            = Budgeting view\nToolTip.ColumnVis                            = \\u6539\\u8B8A\\u6B04\\u4F4D\\u986F\\u793A\nToolTip.ConvertSEntry                        = \\u6539\\u8B8A\\u4EA4\\u6613\\u70BA\\u96D9\\u4EA4\\u6613\\u767B\\u9304\nToolTip.DeleteAccount                        = \\u522A\\u9664\\u5E33\\u6236\nToolTip.DeleteAllExceptFridaySecurityHistory = Delete all Security History except for Fridays\nToolTip.DeleteAllExceptMondaySecurityHistory = Delete all Security History except for Mondays\nToolTip.DeleteAttachment                     = Delete attachment\nToolTip.DeleteWeekendSecurityHistory         = Delete Security History occurring on Saturdays and Sundays\nToolTip.ExportAccountTree                    = Export the List of Accounts to a CSV or XLS file\nToolTip.ExportTransactions                   = Export selected transactions to a CSV or OFX file\nToolTip.FilterAccount                        = \\u7BE9\\u9078\\u5E33\\u6236\nToolTip.FilterAccounts                       = \\u4F9D\\u5E33\\u6236\\u985E\\u578B\\u7BE9\\u9078\nToolTip.FilterMemo                           = Filter by Memo\nToolTip.FilterPayee                          = Filter by Payee\nToolTip.FilterReconciledState                = Filter by Reconciled State\nToolTip.FilterTransactionAge                 = Filter by Transaction Age\nToolTip.FontSize                             = \\u4FEE\\u6539\\u5B57\\u4F53\\u5927\\u5C0F\nToolTip.FuzzyMatch                           = Match is based on the last similar entry\nToolTip.Help                                 = \\u5E2E\\u52A9\nToolTip.ISIN                                 = \\u56FD\\u9645\\u8BC1\\u5238\\u8BC6\\u522B\\u7F16\\u7801\nToolTip.IntegersOnly                         = \\u53EA\\u5141\\u8A31\\u6574\\u6578\nToolTip.ModifyAccount                        = \\u7DE8\\u8F2F\\u5E33\\u6236\nToolTip.NewAccount                           = \\u65B0\\u589E\\u5E33\\u6236\nToolTip.PageSetup                            = \\u9875\\u9762\\u8BBE\\u7F6E\nToolTip.PrintRegRep                          = \\u5217\\u5370\\u767B\\u9304\\u5831\\u8868\nToolTip.ReconcileAccount                     = \\u5C0D\\u5E33/\\u8ABF\\u7BC0\\u5E33\\u6236\nToolTip.Reminders                            = \\u63D0\\u9192\nToolTip.ResizeColumns                        = \\u8ABF\\u6574\\u6B04\\u5BEC\nToolTip.ReversedCredit                       = Reverse the sign of Credit, Liability, Equity, and Income accounts\nToolTip.Scale                                = \\u4F4D\\u6578\\u5230\\u5C0F\\u6578\\u9EDE\\u53F3\\u908A\nToolTip.SelectTags=Select Tags\nToolTip.ShowDetails                          = \\u663E\\u793A\\u660E\\u7EC6\nToolTip.Timestamp                            = \\u4F7F\\u7528 timestamp \\u5EFA\\u7ACB\\u5099\\u4EFD\\u6A94\\u6848/Create a backup with timestamp when closed\nToolTip.ViewAttachment                       = \\u67E5\\u770B\\u9644\\u4EF6\nToolTip.ZoomRegister                         = \\u65B0\\u5E33\\u6236\\u767B\\u9304\n\nTransaction.AddShare        = \\u65B0\\u589E\\u80A1\\u7968\nTransaction.BuyShare        = \\u8CB7\\u80A1\\u7968\nTransaction.Dividend        = \\u5206\\u7D05\nTransaction.DoubleEntry     = \\u96D9\\u8A18\\u5E33\nTransaction.MergeShare      = Stock Merge/\\u6E1B\\u8CC7\nTransaction.ReinvestDiv     = \\u7EA2\\u5229\\u518D\\u6295\\u8D44\nTransaction.RemoveShare     = \\u522A\\u9664\\u80A1\\u7968\nTransaction.ReturnOfCapital = \\u8D44\\u672C\\u56DE\\u62A5\nTransaction.SellShare       = \\u8CE3\\u51FA\\u80A1\\u7968\nTransaction.SingleEntry     = Single Entry/\\u55AE\\u8A18\\u5E33\nTransaction.Split           = Split Transaction/\\u4EA4\\u6613\\u5206\\u5E33\nTransaction.SplitEntry      = Split Transaction Entry/\\u4EA4\\u6613\\u5206\\u5E33\\u9805\\u76EE\nTransaction.SplitShare      = Stock Split/\\u5206\\u5272\\u80A1\\u7968\nTransaction.TransferIn      = Transfer In/\\u532F\\u5165\nTransaction.TransferOut     = Transfer Out/\\u532F\\u51FA\n\nWord.Add             = \\u65B0\\u589E\nWord.All             = All\nWord.Balance         = Balance\nWord.Buy             = \\u8CB7\\u5165\nWord.Commodity       = \\u5546\\u54C1\nWord.Copy            = \\u526F\\u672C\nWord.Description     = \\u63CF\\u8FF0\nWord.Difference      = \\u5DEE\\u989D\nWord.Dividend        = \\u5206\\u7D05\nWord.Exchange        = Exchange\nWord.Fees            = \\u8CBB\\u7528\nWord.GrossExpense    = \\u652F\\u51FA\\u7E3D\\u984D\nWord.GrossIncome     = \\u6536\\u5165\\u7E3D\\u984D\nWord.Interest        = \\u5229\\u606F\nWord.Into            = into\nWord.Invalid         = \\u65E0\\u6548\nWord.Merge           = \\u53BB\nWord.Monthly         = \\u6309\\u6708\\u4EFD\nWord.Name            = \\u540D\\u5B57\nWord.NetIncome       = \\u6DE8\\u6536\\u5165\nWord.NetWorth        = \\u51C0\\u503C\nWord.NewBudget       = \\u65B0\\u9884\\u7B97\nWord.None            = \\u65E0\nWord.Quarterly       = \\u6309\\u5B63\\u5EA6\nWord.ReInvDiv        = Reinvest Dividend\nWord.Remove          = \\u79FB\\u9664\nWord.ReturnOfCapital = \\u8D44\\u672C\\u56DE\\u62A5\nWord.Seconds         = \\u79D2\nWord.Security        = \\u8B49\\u5238\nWord.Sell            = \\u8CE3\\u51FA\nWord.Split           = \\u62C6\\u80A1\nWord.Subtotal        = \\u5C0F\\u8BA1\nWord.Total           = \\u7E3D\\u8A08\nWord.Totals          = \\u7E3D\\u8A08\nWord.Yearly          = \\u6309\\u5E74\\u5EA6\n\nqif = QIF\\u2026\nTitle.RenameTag=Rename Tag\nLabel.RenameTag=Rename Tag to:\nMessage.Error.TagDuplicate=Failed to duplicate the Tag\nWord.NewTag=New Tag\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/ArchiveDate.txt",
    "content": "The cut off date determines where the split occurs between archived transactions and retained transactions.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/ArchiveEquity.txt",
    "content": "A default Equity Account must be selected to transfer opening balances from.\n\nIf an acceptable Equity Account does not exist.  Exit the wizard and create a new Equity Account before continuing.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/ArchiveFile.txt",
    "content": "Vyberte jmeno noveho souboru. Nevybirejte Vas existujici soubor."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/CreateNewFile.txt",
    "content": "Chcete vytvorit novy soubor?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/DupeTransImport.txt",
    "content": "Duplicate transactions were found during the import.\nDo you want to save the duplicate transactions?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/FileNotSaved.txt",
    "content": "Zmeny nebyly ulozeny.\nChcete je ulozit?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/ImportFileZero.txt",
    "content": "Vyberte jGnash 1.11.x nebo novejsi soubor, ktery chcete importovat.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/ImportOne.txt",
    "content": "Vyberte cilovou polozku pro importovane transakce.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/ImportTwo.txt",
    "content": "You can modify the imported transactions here before completing the import.\n\nYou may want to review the destination account to better organize your income and expenses.\n\nNew transactions can be deleted or their import state toggled off.  If a transaction is detected as pre-existing, it may be overridden and the import forced.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/NewFileFour.txt",
    "content": "Vyberte skupiny polozek, ktere nejlepe vystihuji vase potreby. Pridavat, odebirat nebo upravovat polozky muzete kdykoli pozdeji.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/NewFileOne.txt",
    "content": "Zadejte jmeno databaze.\n\nVe vetsine pripadu Vam postaci zakladni nastaveni, ktere mohou pokrocili uzivatele zmenit.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/NewFileThree.txt",
    "content": "Zvolte nize mezi dostupnymi menami. Pokud nechcete vyuzit vice men, ve formulari nic nemente, pridat ci odebrat menu muzete kdykoliv pozdeji. \n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/NewFileTwo.txt",
    "content": "Vyberte vychozi menu, kterou budete nejcasteji pouzivat.  Ve vetsine pripadu bude pouzita jedna mena podle Vaseho lokalniho nastaveni.\n\nVsechny ucty vytvorene v jGnash budou pouzivat tuto menu jako vychozi. Pri vytvareni novych uctu muzete zvolit jine meny.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/QifOne.txt",
    "content": "QIF soubor, ktery jste vybrali blize neurcuje cilovou polozku pro transakce.\nToto je bezne pro QIF soubory prijate z online bankovnictvi a podobne.\n\nVyberte cilovou polozku pro transakce.\nNove transakce budou z duvodu nedostatku informaci obsazenych v QIF souboru vlozeny jako jediny vklad.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/cs/QifTwo.txt",
    "content": "Zde muzete zmenit QIF transakce pred dokoncenim importu.\n\nBude treba prekontrolovat cilovou polozku pro lepsi zorganizovani prijmu a vydaju.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/ArchiveDate.txt",
    "content": "Der Stichtag legt fest, wo die Trennung zwischen archivierten und aufbewahrten Transaktionen liegt.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/ArchiveEquity.txt",
    "content": "Ein Standard-Kapitalkonto muss ausgewählt werden, um Eröffnungssalden zu buchen.\n\nWenn kein akzeptables Kapitalkonto existiert. Verlassen Sie den Assistenten und erzeugen Sie ein Kapitalkonto bevor Sie fortfahren.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/ArchiveFile.txt",
    "content": "Wählen Sie einen Dateinamen für die neue Datei. Wählen Sie nicht die existierende Datei.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/CreateNewFile.txt",
    "content": "Möchten Sie eine neue Datei erzeugen?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/DupeTransImport.txt",
    "content": "Während des Imports wurde Transaktions-Dubletten gefunden.\nMöchten Sie die Transaktions-Dubletten speichern?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/FileNotSaved.txt",
    "content": "Änderungen wurden nicht gespeichert.\nMöchten Sie speichern?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/ImportFileZero.txt",
    "content": "Wählen Sie die Datei (jGnash 1.11.x oder neuer), die importiert werden soll.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/ImportOne.txt",
    "content": "Wählen Sie ein Zielkonto für die importierten Transaktionen."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/ImportTwo.txt",
    "content": "Sie können die importierten Transaktion bearbeiten bevor der Import abgeschlossen wird.\n\nSie sollten das Zielkonto prüfen um Ihre Ein- und Ausgaben besser zu organisieren.\n\nNeue Transaktionen können gelöscht oder deren Importstatus deaktiviert werden. Falls eine Transaktion erkannt wird, die bereits besteht, kann diese überschrieben werden wenn der Import forciert wird."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/NewBudgetOne.txt",
    "content": "Erstellt ein neues Budget anhand der Ein- und Ausgaben der vergangenen Jahre.\r\n\r\nWählen Sie die historische Methode der Wahl um das Budget zu erstellen.\r\n\r\nBudget-Werte können zu einem späteren Zeitpunkt verfeinert werden."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/NewFileFour.txt",
    "content": "Wählen Sie eine Kontenvorlage, die für Sie passend ist. Sie können diese später noch verändern.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/NewFileOne.txt",
    "content": "Geben Sie den Namen der Datenbank ein, die Sie verwenden möchten.\n\nIn den meisten Fällen werden die Vorgaben ausreichend sein. Fortgeschrittene Anwender können sie aber bei Bedarf ändern.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/NewFileThree.txt",
    "content": "Die bei der Erstellung neuer Konten verfügbaren Währungen können unten ausgewählt werden. Wenn Sie nicht mehrere Währungen benutzen wollen, nehmen Sie hier keine Änderungen vor. Währungen können zu jeder Zeit hinzugefügt oder entfernt werden.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/NewFileTwo.txt",
    "content": "Die Standard-Währung sollte die am häufigsten benutzte Währung sein. In den meisten Fällen wird nur eine Währung benutzt (die Landeswährung).\n\nAlle Konten, die in jGnash erzeugt werden, werden als Vorgabe diese Währung benutzen, aber andere Währungen können beim Anlegen neuer Konten gewählt werden.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/QifOne.txt",
    "content": "Die ausgwählte QIF-Datei gibt keine Ziel-Konten für die Transaktionen an.\nDies ist normal bei QIF-Dateien aus Online-Banking oder ähnlichen Quellen.\n\nWählen Sie ein Ziel-Konto für die Transaktionen aus.\nDie neuen Transaktionen werden als Einzelbuchungen erfasst, wegen der fehlenden Informationen aus der QIF-Datei.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/de/QifTwo.txt",
    "content": "Sie können hier die QIF-Transaktionen ändern, bevor der Import fertiggestellt wird.\n\nSie können die Ziel-Konten überprüfen, um die Ausgaben und Einnahmen besser zu organisieren.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/ArchiveDate.txt",
    "content": "The cut off date determines where the split occurs between archived transactions and retained transactions.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/ArchiveEquity.txt",
    "content": "A default Equity Account must be selected to transfer opening balances from.\n\nIf an acceptable Equity Account does not exist.  Exit the wizard and create a new Equity Account before continuing.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/ArchiveFile.txt",
    "content": "Choose a filename for the new file.  Do not choose your existing file.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/CreateNewFile.txt",
    "content": "Would you like to create a new file?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/DupeTransImport.txt",
    "content": "Duplicate transactions were found during the import.\r\nDo you want to save the duplicate transactions?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/FileNotSaved.txt",
    "content": "Changes have not been saved.\r\nDo you want to save?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/ImportFileZero.txt",
    "content": "Select the jGnash 1.11.x or newer file you wish to import."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/ImportOne.txt",
    "content": "Choose a destination account for the imported transactions."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/ImportTwo.txt",
    "content": "You can modify the imported transactions here before completing the import.\n\nYou may want to review the destination account to better organize your income and expenses.\n\nNew transactions can be deleted or their import state toggled off.  If a transaction is detected as pre-existing, it may be overridden and the import forced."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/NewBudgetOne.txt",
    "content": "This builds a new budget based on the prior years income and expenses.\r\n\r\nSelect the historical method of choice for creating a budget.\r\n\r\nBudget amounts may be refined at a later time if needed."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/NewFileFour.txt",
    "content": "Select the account groups that are relevant to your needs.  You can add, remove, and modify accounts as needed later.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/NewFileOne.txt",
    "content": "Enter the the name of the database you would like to use.\r\n\r\nIn most cases, the defaults will be fine, but advanced users can change them if needed."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/NewFileThree.txt",
    "content": "The currencies available for account creation can be selected below.  If you do not wish to use multiple currencies, do not change this form.\n\nCurrencies can be added and removed at any time."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/NewFileTwo.txt",
    "content": "The default currency should be the currency you use most.  In most cases, only one currency will be used which will be your local currency.\r\n\r\nAll accounts created within jGnash will use this currency by default, but other currencies can be selected when creating new accounts."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/QifOne.txt",
    "content": "The QIF file you have selected does not specify a destination account for the transactions. \nThis is normal for QIF files received from online banking and similar sources.\n\nSelect a destination account for the transactions.\nThe new transactions will be Single Entry transactions because of a lack of information supplied in the QIF file."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/en/QifTwo.txt",
    "content": "You may change the QIF transactions here before completing the import.\n\nYou may want to review the destination account to better organize your income and expenses."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/ArchiveDate.txt",
    "content": "La fecha de corte determina en dónde ocurre la división entre las transacciones archivadas y las transacciones nuevas.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/ArchiveEquity.txt",
    "content": "Se debe seleccionar una Cuenta de Patrimonio de la cual transferir el balance inicial.\n\nSi la Cuenta de Patrimonio no existe, salga del Asistente y agregue una antes de continuar.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/ArchiveFile.txt",
    "content": "Ingrese el nombre del archivo. No escoja un nombre archivo existente.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/CreateNewFile.txt",
    "content": "¿Desea crear un nuevo archivo?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/DupeTransImport.txt",
    "content": "Se encontraron transacciones duplicadas durante la importación.\n¿Desea guardar estas transacciones duplicadas?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/FileNotSaved.txt",
    "content": "Todavía no ha guardado los cambios.\n¿Desea guardarlos ahora?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/NewFileFour.txt",
    "content": "Seleccione los grupos de cuenta que son pertinentes a sus necesidades. Usted puede agregar, quitar y modificar las cuentas m\\u00E1s tarde si necesita.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/NewFileThree.txt",
    "content": "Seleccione las monedas para crear las cuentas. Si no desea usar varias monedas, no haga ningún cambio. Puede agregar y remover monedas posteriormente.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/NewFileTwo.txt",
    "content": "La moneda predeterminada debería ser la que usted usa normalmente. En la mayoría de casos, se usa solo una moneda, especificamente, su moneda local.\n\nTodas las cuentas creadas en jGnash usarán esta moneda de modo predeterminado, pero otras monedas pueden ser escogidas al crear nuevas cuentas.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/QifOne.txt",
    "content": "El archivo QIF que ha seleccionado no especifica una cuenta de destino para las transacciones\nEsto es normal para archivos QIF que son recibidos electrónicamente desde bancos y fuentes similares.\n\nSeleccione una cuenta de destino para las transacciones.\nLas nuevas transacciones serán de entrada simple, debido a la falta de información que provee el archivo QIF.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/es/QifTwo.txt",
    "content": "Puede cambiar las transacciones QIF antes de completar la importación.\n\nQuiza desee revisar las cuentas de destino para organizar mejor sus ingresos y gastos\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/ArchiveDate.txt",
    "content": "La date limite détermine la frontière entre la période pour laquelle les transactions sont archivées et la période pour laquelle les transactions sont gardées. \n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/ArchiveEquity.txt",
    "content": "Un compte de capitaux propres doit être sélectionné pour transférer les soldes initiaux.\n\nS'il n'y a pas de compte de capitaux propres, arrêter la procédure et créer un compte de capitaux propres avant de reprendre cette procédure.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/ArchiveFile.txt",
    "content": "Choisir un nom pour le nouveau fichier. Ne pas choisir le nom d'un fichier existant.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/CreateNewFile.txt",
    "content": "Voulez-vous créer un nouveau fichier?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/DupeTransImport.txt",
    "content": "Des transactions redondantes ont été trouvées durant l'importation.\nSouhaitez-vous sauver ces doublons?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/FileNotSaved.txt",
    "content": "Les modifications n'ont pas été sauvegardées.\nVoulez-vous les enregistrer?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/NewFileFour.txt",
    "content": "Select the account groups that are relevant to your needs.  You can add, remove, and modify accounts as needed later.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/NewFileThree.txt",
    "content": "Les devises nécessaires pour la création de nouveaux comptes peuvent être sélectionnées ci-dessous. Si vous ne souhaitez pas utiliser plusieurs devises simultanément, ne changez rien. Des devises peuvent être ajoutées ou supprimées à tout moment.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/NewFileTwo.txt",
    "content": "La devise par défaut est la devise que vous utilisez le plus souvent. Dans la plupart des cas, la seule devise à définir est la devise de votre pays.\n\nTous les comptes créés dans jGnash utiliseront par défaut cette devise, mais d'autres devises peuvent être utilisées lors de la création de nouveaux comptes.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/QifOne.txt",
    "content": "Le fichier QIF sélectionné ne contient pas de compte de destination pour les transactions. Ce format est normal pour les fichiers QIF récupérés auprès de services bancaires en ligne ou assimilés.\n\nSélectionnez un compte de destination pour les transactions.\nLes nouvelles transactions seront du type \"Entrée simple\" car les informations fournies par le fichier QIF sont incomplètes. \n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/fr/QifTwo.txt",
    "content": "Vous pouvez changer les transactions QIF avant de terminer l'importation.\n\nVous pouvez revoir la destination des transactions pour améliorer l'organisation de vos revenus et dépenses.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/CreateNewFile.txt",
    "content": "Creare un nuovo file?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/FileNotSaved.txt",
    "content": "Le modifiche non sono state salvate.\r\r\nSalvare adesso?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/NewFileFour.txt",
    "content": "Selezionate il piano dei conti che più si avvicina alle vostre necessità. La lista selezionata può comunque essere successivamente modificata.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/NewFileThree.txt",
    "content": "Le valute disponibili per la creazione del conto posso essere selezionate qui sotto. Se non si vogliono usare più valute, basta non modificare questo formulario. Le valute possono essere aggiunte o rimosse in ogni momento.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/NewFileTwo.txt",
    "content": "La valuta predefinita dovrebbe essere la valuta più utilizzata. Nella maggior parte dei casi viene usata solo una valuta, cioè quella locale.\n\n\n\nTutti i conti creati in jGnash useranno questa valuta come predefinita, ma valute supplementari potranno essere definite durante la creazione di nuovi conti.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/QifOne.txt",
    "content": "Il file QIF selezionato non specifica un conto di destinazione per le transazioni.\nCiò è normale per files QIF ricevuti da sistemi bancari online o sergenti simili.\n\nSelezionare un conto di destinazione per le transazioni.\nLe nuove transazioni verranno trattate come importi singoli a causa di mancanza di informazioni nel file QIF.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/it/QifTwo.txt",
    "content": "È possibile modificare le transazioni QIF prima di importare i dati.\n\nBisognerebbe controllare il conto di destinazione per organizzare meglio il reddito e le spese.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/ArchiveDate.txt",
    "content": "Ribin\\u0117 data nusako rib\\u0105 tarp suarchyvuot\\u0173 ir tarp likusi\\u0173 operacij\\u0173.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/ArchiveEquity.txt",
    "content": "Reikia nurodyti \\u012fprastin\\u0119 \\u012fprastin\\u0117 akcij\\u0173 s\\u0105skait\\u0105, i\\u0161 kurios b\\u016bt\\u0173 galima pervesti neu\\u017ebaigtus balansus.\n\nJei tinkamos s\\u0105skaitos n\\u0117ra, u\\u017edarykite vedl\\u012f ir sukurkite j\\u0105 prie\\u0161 t\\u0119sdami proces\\u0105.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/ArchiveFile.txt",
    "content": "Parinkite nauj\\u0105 bylos pavadinim\\u0105. Tik nepasirinkite jau sukurtos bylos.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/CreateNewFile.txt",
    "content": "Ar norite sukurti nauj\\u0105 byl\\u0105?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/FileNotSaved.txt",
    "content": "J\\u016bs\\u0173 daryti pakeitimai nei\\u0161saugoti byloje.\nAr norite juos i\\u0161saugoti?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/NewFileFour.txt",
    "content": "Select the account groups that are relevant to your needs.  You can add, remove, and modify accounts as needed later.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/NewFileThree.txt",
    "content": "\\u017demiau galima pa\\u017eym\\u0117ti visas valiutas, kurias sistema leis pasirinkti kuriant s\\u0105skaitas. Jei nenorite naudoti keleto valiut\\u0173 savo sistemoje, tiesiog \\u0161ioje formoje nieko nekeiskite. Valiutos gali b\\u016bti prid\\u0117tos ar pa\\u0161alintos ir v\\u0117liau, jau naudojantis sistema. \n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/NewFileTwo.txt",
    "content": "\\u012eprastin\\u0117 valiuta, tai valiuta, kuri\\u0105 naudojate da\\u017eniausiai. Da\\u017eniausiai tik viena valiuta naudojama vietiniams atsiskaitymams.\n\nVisos s\\u0105skaitos, sukurtos jGnash sistemoje pagal nutyl\\u0117jim\\u0105 naudos \\u0161i\\u0105 valiut\\u0105, bet kuriant s\\u0105skait\\u0105 galima nurodyti ir kit\\u0105 valiut\\u0105.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/QifOne.txt",
    "content": "J\\u016bs\\u0173 pasirinktoje QIF byloje esan\\u010dioms operacijoms nenurodyta s\\u0105skaita. \nTai \\u012fprasta QIF byloms, gautoms i\\u0161 internetin\\u0117s bankininkyst\\u0117s ar kit\\u0173 pana\\u0161i\\u0173 sistem\\u0173.\n\nNurodykite operacij\\u0173 s\\u0105skait\\u0105.\nNaujos operacijos bus \\u012fvestos kaip atskiros, kadangi QIF byloje nepateikiama informacija, reikalinga operacij\\u0173 grupavimui.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/lt/QifTwo.txt",
    "content": "Prie\\u0161 importuojant QIF operacijas dar galite jas pakeisti.\n\nGal b\\u016bt, siekdami patobulinti pajam\\u0173 ir i\\u0161laid\\u0173 i\\u0161d\\u0117stym\\u0105, nor\\u0117site per\\u017ei\\u016br\\u0117ti s\\u0105skaitas \\u012f kurias papuls operacijose pateiktos sumos.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/ArchiveDate.txt",
    "content": "De archiveringsdatum bepaalt de splitsingsdatum tussen gearchiveerde transacties en transacties die behouden blijven.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/ArchiveEquity.txt",
    "content": "Een standaard Equity rekening moet geselecteerd worden om de openingsbalansen te kunnen maken.\n\nAls er geen bruikbare Equity rekening bestaat, moet je eerst de wizard verlaten en een bruikbare Equity rekening aanmaken.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/ArchiveFile.txt",
    "content": "Kies een bestandsnaam voor het nieuw aan te maken archiveringsbestand. Hiervoor kun je niet de huidige bestandsnaam kiezen, die wordt gebruikt voor de niet te archiveren transacties.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/CreateNewFile.txt",
    "content": "Wil je een nieuw bestand aanmaken?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/DupeTransImport.txt",
    "content": "Tijdens het importeren zijn er dubbele transacties gevonden.\r\nWil je de dubbele transacties bewaren?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/FileNotSaved.txt",
    "content": "Wijzigingen zijn niet opgeslagen.\r\nWil je de wijzigingen opslaan?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/NewFileFour.txt",
    "content": "Select the account groups that are relevant to your needs.  You can add, remove, and modify accounts as needed later.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/NewFileThree.txt",
    "content": "De beschikbare valuta voor nieuw aan te maken rekeningen kunnen hier gekozen worden. Als je geen gebruik wilt maken van meerdere verschillende valuta's, wijzig hier dan niets.\nValuta kunnen op elk moment worden aangemaakt en verwijderd.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/NewFileTwo.txt",
    "content": "De standaard valuta kun je het beste instellen op de valuta die je het vaakst gebruikt. In het algemeen zal er slechts \\u00e9\\u00e9n valuta gebruikt worden, namelijk je plaatselijke valuta (waarschijnlijk de Euro).\r\n\r\nAlle rekeningen die in jGnash worden aangemaakt zullen standaard deze valuta gebruiken, maar bij het aanmaken van rekeningen kun je ook andere valuta selecteren.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/QifOne.txt",
    "content": "Het geselecteerde QIF bestand specificeert geen doelrekening voor de transacties.\nDit is normaal voor QIF bestanden die je van online banking of soortgelijke bronnen ontvangt.\n\nSelecteer een doelrekening voor de transacties.\nDe nieuwe transacties zullen Single Entry transacties zijn, omdat er niet voldoende gegevens worden meegeleverd in het QIF bestand.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/nl/QifTwo.txt",
    "content": "Je kunt de QIF transacties nu wijzigen voordat het importeren wordt voltooid.\n\nWaarschijnlijk zul je de doelrekening moeten aanpassen om je inkomsten en uitgaven beter de ordenen.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/ArchiveDate.txt",
    "content": "Data odcięcia wskazuje, kiedy nastąpi podział między transakcjami archiwalnymi a zachowanymi.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/ArchiveEquity.txt",
    "content": "Aby przenieść saldo bilansu otwarcia, należy wybrać domyślne konto kapitału.\n\nJeśli nie istnieje akceptowalne konto kapitału, zanim będziesz kontynuować opuść asystenta i stwórz nowe konto kapitału.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/ArchiveFile.txt",
    "content": "Wybierz nazwę pliku dla nowego pliku. Nie wybieraj istniejącego pliku.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/CreateNewFile.txt",
    "content": "Czy chcesz stworzyć nowy plik?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/DupeTransImport.txt",
    "content": "Podczas importu znaleziono zduplikowane transakcje.\nCzy chcesz zapamiętać zduplikowane transakcje?"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/FileNotSaved.txt",
    "content": "Zmiany nie zostały zapamiętane.\nCzy chcesz je zapamiętać?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/ImportFileZero.txt",
    "content": "Wybierz plik jGnash 1.11.x lub nowszy, który ma zostać zaimportowany.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/ImportOne.txt",
    "content": "Wybierz konto docelowe dla importowanych transakcji.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/ImportTwo.txt",
    "content": "Tutaj możesz modyfikować importowane transakcje przed zakończeniem importu.\n\nAby lepiej zorganizować swoje przychody i wydatki, możesz chcieć przejrzeć konto docelowe.\n\nNowe transakcje mogą zostać usunięte lub wyłączone z importu. Jeśli transakcja zostanie wykryta jako już istniejąca, może zostać zastąpiona a import wymuszony."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/NewBudgetOne.txt",
    "content": "To stworzy nowy budżet opierając się na zeszłorocznych dochodach i wydatkach.\n\nAby stworzyć budżet, wybierz historyczną metodę wyboru.\n\nJeśli potrzeba, kwoty w budżecie mogą zostać później poprawione."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/NewFileFour.txt",
    "content": "Wybierz grupy kont, które są odpowiednie dla twoich potrzeb. Jeśli potrzeba, później możesz dodać, usunąć lub zmienić konta.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/NewFileOne.txt",
    "content": "Wprowadź nazwę bazy danych, której chciałbyś użyć.\n\nW większości przypadków wystarczy nazwa domyślna. Jeśli potrzeba, użytkownicy zaawansowani mogą ją jednak zmienić."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/NewFileThree.txt",
    "content": "Waluty dostępne podczas tworzenia nowych kont mogą zostać wybrane poniżej. Jeśli nie chcesz używać wielu walut, nie zmieniaj tego wyboru.\n\nWaluty można dodać lub usunąć w dowolnym czasie."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/NewFileTwo.txt",
    "content": "Walutą domyślną powinna być waluta, której używasz najczęściej. W większości przypadków używana będzie tylko jedna waluta, która jest twoją walutą lokalną.\n\nWszystkie konta stworzone w jGnash będą używać waluty domyślnej, ale podczas tworzenia konta można wybrać inną walutę."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/QifOne.txt",
    "content": "Wybrany plik QIF nie wskazuje konta docelowego dla transakcji. \nTo jest typowe dla plików QIF otrzymanych z bankowości online lub podobnych źródeł.\n\nWybierz konto docelowe dla transakcji.\nNowe transakcje będą transakcjami jednostronnymi z powodu braku informacji dostarczonej w pliku QIF."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pl/QifTwo.txt",
    "content": "Tutaj możesz zmienić transakcje QIF przed zakończeniem importu.\n\nAby lepiej zorganizować swoje dochody i wydatki, możesz chcieć przejrzeć konto docelowe."
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/ArchiveDate.txt",
    "content": "A data limite determina onde a divisão ocorre entre as transações arquivadas e as transações mantidas.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/ArchiveEquity.txt",
    "content": "Uma conta de Patrimônio Líquido precisa ser selecionada para transferência do balanço inicial.\n\nSe um conta de Patrimônio Líquido aceitável não existe. Saia do assistente e crie uma nova conta de Patrimônio Líquido antes de continuar.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/ArchiveFile.txt",
    "content": "Escolha um nome para o novo arquivo. Não escolha um arquivo existente.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/CreateNewFile.txt",
    "content": "Você gostaria de criar um novo arquivo?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/DupeTransImport.txt",
    "content": "Transações duplicadas foram encontradas durante a importação.\nVocê quer salvar as transações duplicadas?\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/FileNotSaved.txt",
    "content": "As alterações não foram salvas.\r\nVocê deseja salvar?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/ImportFileZero.txt",
    "content": "Selecione um arquivo jGnash 1.11.x ou mais novo que você deseja importar.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/ImportOne.txt",
    "content": "Escolha uma conta de destino para as transações importadas.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/ImportTwo.txt",
    "content": "Você pode modificar aqui as transações importadas antes de completar a importação.\n\nVocê pode querer revisar a conta de destino para melhor organizar as suas receitas e despesas.\n\nNovas transações podem ser apagadas ou seu estado de importação mudado para não. Se uma transação é detectada como pré-existente, ela pode ser sobrescrita e a importação forçada.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/NewFileFour.txt",
    "content": "Seleciona os grupos de contas que são relevantes para suas necessidades. Você pode adicionar, remover e modificar as contas depois, conforme a necessidade.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/NewFileOne.txt",
    "content": "Entre o nome do banco de dados que você gostaria de usar.\n\nNa maioria dos casos, os padrões serão suficientes, mas os usuários avançados podem alterá-las se necessário.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/NewFileThree.txt",
    "content": "As moedas disponíveis para a criação da conta podem ser selecionadas abaixo. Se você não deseja usar multiplas moedas, não altere este formulário. Moedas podem ser adicionadas e removidas a qualquer momento.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/NewFileTwo.txt",
    "content": "A moeda padrão deve ser a moeda que você mais usa. Na maioria dos casos, apenas uma moeda será utilizada, que será a sua moeda local.\r\n\r\nTodas as contas criadas no jGnash usarão esta moeda por padrão, mas outras moedas podem ser selecionadas quando uma nova conta for criada.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/QifOne.txt",
    "content": "O arquivo QIF que você selecionou não especifica a conta de destino para as transações.\nIsto é normal para aquivos QIF recebidos de bancos online ou fontes semelhantes.\n\nSelecione a conta de destino para as transações.\nAs novas transações serão transações de entrada única pela falta de informação fornecida no arquivo QIF.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/pt/QifTwo.txt",
    "content": "Você pode alterar as transações QIF aqui antes de completar a importação.\n\nVocê pode revisar a conta de destino para uma melhor organização de suas receitas e despesas.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/ArchiveDate.txt",
    "content": "\\u0414\\u0430\\u0442\\u0430 \\u0444\\u0438\\u043a\\u0441\\u0438\\u0440\\u043e\\u0432\\u0430\\u043d\\u043d\\u043e\\u0433\\u043e \\u0431\\u0430\\u043b\\u0430\\u043d\\u0441\\u0430 \\u043e\\u0442\\u0434\\u0435\\u043b\\u044f\\u0435\\u0442 \\u0430\\u0440\\u0445\\u0438\\u0432 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439 \\u0441 \\u043f\\u043e\\u0441\\u0447\\u0438\\u0442\\u0430\\u043d\\u043d\\u044b\\u043c \\u0431\\u0430\\u043b\\u0430\\u043d\\u0441\\u043e\\u043c, \\u043e\\u0442 \\u043d\\u043e\\u0432\\u044b\\u0445 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/ArchiveEquity.txt",
    "content": "\\u0414\\u043b\\u044f \\u043f\\u043e\\u043b\\u0443\\u0447\\u0435\\u043d\\u0438\\u044f \\u043d\\u0430\\u0447\\u0430\\u043b\\u044c\\u043d\\u043e\\u0433\\u043e \\u0431\\u0430\\u043b\\u0430\\u043d\\u0441\\u0430 \\u0434\\u043e\\u043b\\u0436\\u0435\\u043d \\u0431\\u044b\\u0442\\u044c \\u0432\\u044b\\u0431\\u0440\\u0430\\u043d \\u0441\\u0447\\u0435\\u0442 \\u0430\\u043a\\u0446\\u0438\\u0439.\r\n\r\n\\u0415\\u0441\\u043b\\u0438 \\u0442\\u0430\\u043a\\u043e\\u0433\\u043e \\u0441\\u0447\\u0435\\u0442\\u0430 \\u043d\\u0435 \\u0441\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0443\\u0435\\u0442, \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0435 \\u0440\\u0430\\u0431\\u043e\\u0442\\u0443 \\u0441 \\u041f\\u043e\\u043c\\u043e\\u0449\\u043d\\u0438\\u043a\\u043e\\u043c \\u0438 \\u0441\\u043e\\u0437\\u0434\\u0430\\u0439\\u0442\\u0435 \\u0442\\u0430\\u043a\\u043e\\u0439 \\u0441\\u0447\\u0435\\u0442, \\u043f\\u0440\\u0435\\u0436\\u0434\\u0435 \\u0447\\u0435\\u043c \\u043f\\u0440\\u043e\\u0434\\u043e\\u043b\\u0436\\u0430\\u0442\\u044c.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/ArchiveFile.txt",
    "content": "\\u041f\\u043e\\u0436\\u0430\\u043b\\u0443\\u0439\\u0441\\u0442\\u0430, \\u0432\\u044b\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u0438\\u043c\\u044f \\u0434\\u043b\\u044f \\u043d\\u043e\\u0432\\u043e\\u0433\\u043e \\u0444\\u0430\\u0439\\u043b\\u0430. \\u041d\\u0435 \\u0432\\u044b\\u0431\\u0438\\u0440\\u0430\\u0439\\u0442\\u0435 \\u0441\\u0443\\u0449\\u0435\\u0441\\u0442\\u0432\\u0443\\u044e\\u0449\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043b!\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/CreateNewFile.txt",
    "content": "\\u0421\\u043e\\u0437\\u0434\\u0430\\u0442\\u044c \\u043d\\u043e\\u0432\\u044b\\u0439 \\u0444\\u0430\\u0439\\u043b?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/DupeTransImport.txt",
    "content": "\\u0412 \\u043f\\u0440\\u043e\\u0446\\u0435\\u0441\\u0441\\u0435 \\u0438\\u043c\\u043f\\u043e\\u0440\\u0442\\u0430 \\u0431\\u044b\\u043b\\u0438 \\u043e\\u0431\\u043d\\u0430\\u0440\\u0443\\u0436\\u0435\\u043d\\u044b \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438-\\u0434\\u0443\\u0431\\u043b\\u0438\\u043a\\u0430\\u0442\\u044b.\r\n\\u0416\\u0435\\u043b\\u0430\\u0435\\u0442\\u0435 \\u0437\\u0430\\u043f\\u0438\\u0441\\u0430\\u0442\\u044c \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438-\\u0434\\u0443\\u0431\\u043b\\u0438\\u043a\\u0430\\u0442\\u044b?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/FileNotSaved.txt",
    "content": "\\u0424\\u0430\\u0439\\u043b \\u0431\\u044b\\u043b \\u0438\\u0437\\u043c\\u0435\\u043d\\u0435\\u043d.\r\n\\u0412\\u044b \\u0445\\u043e\\u0442\\u0438\\u0442\\u0435 \\u0441\\u043e\\u0445\\u0440\\u0430\\u043d\\u0438\\u0442\\u044c \\u0438\\u0437\\u043c\\u0435\\u043d\\u0435\\u043d\\u0438\\u044f?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/NewFileFour.txt",
    "content": "Select the account groups that are relevant to your needs.  You can add, remove, and modify accounts as needed later.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/NewFileThree.txt",
    "content": "\\u0417\\u0434\\u0435\\u0441\\u044c \\u0412\\u044b \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0432\\u044b\\u0431\\u0440\\u0430\\u0442\\u044c \\u0432\\u0430\\u043b\\u044e\\u0442\\u044b, \\u043a\\u043e\\u0442\\u043e\\u0440\\u044b\\u0435 \\u0431\\u0443\\u0434\\u0443\\u0442 \\u0434\\u043e\\u0441\\u0442\\u0443\\u043f\\u043d\\u044b \\u043f\\u0440\\u0438 \\u0441\\u043e\\u0437\\u0434\\u0430\\u043d\\u0438\\u0438 \\u0441\\u0447\\u0435\\u0442\\u043e\\u0432. \\u0415\\u0441\\u043b\\u0438 \\u0412\\u044b \\u043d\\u0435 \\u0431\\u0443\\u0434\\u0435\\u0442\\u0435 \\u0438\\u0441\\u043f\\u043e\\u043b\\u044c\\u0437\\u043e\\u0432\\u0430\\u0442\\u044c \\u043d\\u0435\\u0441\\u043a\\u043e\\u043b\\u044c\\u043a\\u043e \\u0432\\u0430\\u043b\\u044e\\u0442, \\u043d\\u0435 \\u0438\\u0437\\u043c\\u0435\\u043d\\u044f\\u0439\\u0442\\u0435 \\u044d\\u0442\\u0443 \\u0444\\u043e\\u0440\\u043c\\u0443. \\u0412\\u044b \\u0441\\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0434\\u043e\\u0431\\u0430\\u0432\\u0438\\u0442\\u044c \\u0438 \\u0443\\u0434\\u0430\\u043b\\u0438\\u0442\\u044c \\u0432\\u0430\\u043b\\u044e\\u0442\\u044b \\u0432 \\u043b\\u044e\\u0431\\u043e\\u0435 \\u0432\\u0440\\u0435\\u043c\\u044f.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/NewFileTwo.txt",
    "content": "\\u0412\\u0430\\u043b\\u044e\\u0442\\u0430 \\u043f\\u043e \\u0443\\u043c\\u043e\\u043b\\u0447\\u0430\\u043d\\u0438\\u044e -- \\u044d\\u0442\\u043e \\u0432\\u0430\\u043b\\u044e\\u0442\\u0430, \\u043a\\u043e\\u0442\\u043e\\u0440\\u0443\\u044e \\u0412\\u044b \\u0431\\u0443\\u0434\\u0435\\u0442\\u0435 \\u0438\\u0441\\u043f\\u043e\\u043b\\u044c\\u0437\\u043e\\u0432\\u0430\\u0442\\u044c \\u0447\\u0430\\u0449\\u0435 \\u0432\\u0441\\u0435\\u0433\\u043e. \r\n\\u0421\\u0447\\u0435\\u0442\\u0430 \\u0431\\u0443\\u0434\\u0443\\u0442 \\u0441\\u043e\\u0437\\u0434\\u0430\\u0432\\u0430\\u0442\\u044c\\u0441\\u044f \\u043f\\u043e \\u0443\\u043c\\u043e\\u043b\\u0447\\u0430\\u043d\\u0438\\u044e \\u0432 \\u044d\\u0442\\u043e\\u0439 \\u0432\\u0430\\u043b\\u044e\\u0442\\u0435, \\u043d\\u043e \\u0412\\u044b \\u0441\\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0432\\u044b\\u0431\\u0440\\u0430\\u0442\\u044c \\u0438 \\u0434\\u0440\\u0443\\u0433\\u0438\\u0435 \\u0432\\u0430\\u043b\\u044e\\u0442\\u044b \\u043f\\u0440\\u0438 \\u0441\\u043e\\u0437\\u0434\\u0430\\u043d\\u0438\\u0438 \\u0441\\u0447\\u0435\\u0442\\u0430.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/QifOne.txt",
    "content": "\\u0412 \\u0438\\u043c\\u043f\\u043e\\u0440\\u0442\\u0438\\u0440\\u0443\\u0435\\u043c\\u043e\\u043c QIF-\\u0444\\u0430\\u0439\\u043b\\u0435 \\u043d\\u0435 \\u0443\\u043a\\u0430\\u0437\\u0430\\u043d\\u044b \\u043a\\u043e\\u0440\\u0440\\u0435\\u0441\\u043f\\u043e\\u043d\\u0434\\u0438\\u0440\\u0443\\u044e\\u0449\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430 (\\u0441\\u0447\\u0435\\u0442\\u0430 \\u043d\\u0430\\u0437\\u043d\\u0430\\u0447\\u0435\\u043d\\u0438\\u044f).\r\n\\u042d\\u0442\\u043e \\u043d\\u043e\\u0440\\u043c\\u0430\\u043b\\u044c\\u043d\\u0430\\u044f \\u0441\\u0438\\u0442\\u0443\\u0430\\u0446\\u0438\\u044f \\u0434\\u043b\\u044f QIF-\\u0444\\u0430\\u0439\\u043b\\u043e\\u0432, \\u043f\\u043e\\u043b\\u0443\\u0447\\u0435\\u043d\\u043d\\u044b\\u0445 \\u043e\\u0442 \\u044d\\u043b\\u0435\\u043a\\u0442\\u0440\\u043e\\u043d\\u043d\\u044b\\u0445 \\u0431\\u0430\\u043d\\u043a\\u043e\\u0432\\u0441\\u043a\\u0438\\u0445 \\u0441\\u0438\\u0441\\u0442\\u0435\\u043c \\u0438 \\u0438\\u0437 \\u043f\\u0440\\u043e\\u0447\\u0438\\u0445 \\u043f\\u043e\\u0434\\u043e\\u0431\\u043d\\u044b\\u0445 \\u0438\\u0441\\u0442\\u043e\\u0447\\u043d\\u0438\\u043a\\u043e\\u0432.\r\n\r\n\\u041f\\u043e\\u0436\\u0430\\u043b\\u0443\\u0439\\u0441\\u0442\\u0430, \\u0432\\u044b\\u0431\\u0435\\u0440\\u0438\\u0442\\u0435 \\u043a\\u043e\\u0440\\u0440\\u0435\\u0441\\u043f\\u043e\\u043d\\u0434\\u0438\\u0440\\u0443\\u044e\\u0449\\u0438\\u0439 \\u0441\\u0447\\u0435\\u0442 \\u0434\\u043b\\u044f \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0439.\r\n\\u041d\\u043e\\u0432\\u044b\\u0435 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438 \\u0431\\u0443\\u0434\\u0443\\u0442 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u044f\\u043c\\u0438 \\u0441 \\u043f\\u0440\\u043e\\u0441\\u0442\\u043e\\u0439 \\u0437\\u0430\\u043f\\u0438\\u0441\\u044c\\u044e, \\u0442\\u0430\\u043a \\u043a\\u0430\\u043a \\u0432 QIF-\\u0444\\u0430\\u0439\\u043b\\u0435 \\u043d\\u0435\\u0434\\u043e\\u0441\\u0442\\u0430\\u0442\\u043e\\u0447\\u043d\\u043e \\u0438\\u043d\\u0444\\u043e\\u0440\\u043c\\u0430\\u0446\\u0438\\u0438 \\u0434\\u043b\\u044f \\u0434\\u0432\\u043e\\u0439\\u043d\\u043e\\u0439 \\u0437\\u0430\\u043f\\u0438\\u0441\\u0438.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/ru/QifTwo.txt",
    "content": "\\u0421\\u0435\\u0439\\u0447\\u0430\\u0441 \\u0412\\u044b \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0438\\u0437\\u043c\\u0435\\u043d\\u0438\\u0442\\u044c \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0438\\u0438, \\u0438\\u043c\\u043f\\u043e\\u0440\\u0442\\u0438\\u0440\\u043e\\u0432\\u0430\\u043d\\u043d\\u044b\\u0435 \\u0438\\u0437 QIF-\\u0444\\u0430\\u0439\\u043b\\u0430.\r\n\\u0412\\u043e\\u0437\\u043c\\u043e\\u0436\\u043d\\u043e, \\u0412\\u0430\\u043c \\u0441\\u0442\\u043e\\u0438\\u0442 \\u043f\\u0440\\u043e\\u0432\\u0435\\u0440\\u0438\\u0442\\u044c \\u043a\\u043e\\u0440\\u0440\\u0435\\u0441\\u043f\\u043e\\u043d\\u0434\\u0438\\u0440\\u0443\\u044e\\u0449\\u0438\\u0435 \\u0441\\u0447\\u0435\\u0442\\u0430, \\u0447\\u0442\\u043e\\u0431\\u044b \\u043b\\u0443\\u0447\\u0448\\u0435 \\u0443\\u043f\\u043e\\u0440\\u044f\\u0434\\u043e\\u0447\\u0438\\u0442\\u044c \\u0432\\u0430\\u0448\\u0438 \\u0434\\u043e\\u0445\\u043e\\u0434\\u044b \\u0438 \\u0440\\u0430\\u0441\\u0445\\u043e\\u0434\\u044b.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/ArchiveDate.txt",
    "content": "\\u0414\\u0430\\u0442\\u0430 \\u0444\\u0456\\u043a\\u0441\\u043e\\u0432\\u0430\\u043d\\u043e\\u0433\\u043e \\u0431\\u0430\\u043b\\u0430\\u043d\\u0441\\u0443 \\u0432\\u0456\\u0434\\u0434\\u0456\\u043b\\u044f\\u0454 \\u0430\\u0440\\u0445\\u0456\\u0432 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439 \\u0432\\u0456\\u0434 \\u043d\\u043e\\u0432\\u0438\\u0445 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/ArchiveEquity.txt",
    "content": "\\u0414\\u043b\\u044f \\u043e\\u0442\\u0440\\u0438\\u043c\\u0430\\u043d\\u043d\\u044f \\u043f\\u043e\\u0447\\u0430\\u0442\\u043a\\u043e\\u0432\\u043e\\u0433\\u043e \\u0431\\u0430\\u043b\\u0430\\u043d\\u0441\\u0443 \\u043f\\u043e\\u0442\\u0440\\u0456\\u0431\\u043d\\u043e \\u0432\\u0438\\u0431\\u0440\\u0430\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043e\\u043a \\u043f\\u043e\\u0447\\u0430\\u0442\\u043a\\u043e\\u0432\\u043e\\u0433\\u043e \\u043a\\u0430\\u043f\\u0456\\u0442\\u0430\\u043b\\u0443.\r\n\r\n\\u042f\\u043a\\u0449\\u043e \\u0442\\u0430\\u043a\\u043e\\u0433\\u043e \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0443 \\u043d\\u0435 \\u0456\\u0441\\u043d\\u0443\\u0454, \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0456\\u0442\\u044c \\u0440\\u043e\\u0431\\u043e\\u0442\\u0443 \\u0437 \\u041f\\u043e\\u043c\\u0456\\u0447\\u043d\\u0438\\u043a\\u043e\\u043c \\u0442\\u0430 \\u043f\\u0435\\u0440\\u0448 \\u043d\\u0456\\u0436 \\u043f\\u0440\\u043e\\u0434\\u043e\\u0432\\u0436\\u0443\\u0432\\u0430\\u0442\\u0438, \\u0441\\u0442\\u0432\\u043e\\u0440\\u0456\\u0442\\u044c \\u0442\\u0430\\u043a\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043e\\u043a.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/ArchiveFile.txt",
    "content": "\\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044c, \\u0431\\u0443\\u0434\\u044c \\u043b\\u0430\\u0441\\u043a\\u0430 \\u0456\\u043c'\\u044f \\u0434\\u043b\\u044f \\u043d\\u043e\\u0432\\u043e\\u0433\\u043e \\u0444\\u0430\\u0439\\u043b\\u0443. \\u041d\\u0435 \\u0432\\u0438\\u0431\\u0438\\u0440\\u0430\\u0439\\u0442\\u0435 \\u0456\\u0441\\u043d\\u0443\\u044e\\u0447\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043b\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/CreateNewFile.txt",
    "content": "\\u0421\\u0442\\u0432\\u043e\\u0440\\u0438\\u0442\\u0438 \\u043d\\u043e\\u0432\\u0438\\u0439 \\u0444\\u0430\\u0439\\u043b?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/DupeTransImport.txt",
    "content": "\\u0412 \\u043f\\u0440\\u043e\\u0446\\u0435\\u0441\\u0456 \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u0443 \\u0431\\u0443\\u043b\\u0438 \\u0432\\u0438\\u044f\\u0432\\u043b\\u0435\\u043d\\u0456 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457-\\u0434\\u0443\\u0431\\u043b\\u0456\\u043a\\u0430\\u0442\\u0438.\r\n\\u0411\\u0430\\u0436\\u0430\\u0454\\u0442\\u0435 \\u0457\\u0445 \\u0437\\u0430\\u043f\\u0438\\u0441\\u0430\\u0442\\u0438?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/FileNotSaved.txt",
    "content": "\\u0424\\u0430\\u0439\\u043b \\u0431\\u0443\\u043b\\u043e \\u0437\\u043c\\u0456\\u043d\\u0435\\u043d\\u043e.\r\n\\u0411\\u0430\\u0436\\u0430\\u0454\\u0442\\u0435 \\u0437\\u0431\\u0435\\u0440\\u0456\\u0433\\u0442\\u0438 \\u0437\\u043c\\u0456\\u043d\\u0438?\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/ImportFileZero.txt",
    "content": "\\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044c \\u0444\\u0430\\u0439\\u043b jGnash \\u0432\\u0435\\u0440\\u0441\\u0456\\u0457 1.11.x \\u0447\\u0438 \\u043d\\u043e\\u0432\\u0456\\u0448\\u0435 \\u0434\\u043b\\u044f \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u0443.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/ImportOne.txt",
    "content": "\\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044c \\u0440\\u0430\\u0445\\u0443\\u043d\\u043e\\u043a, \\u043d\\u0430 \\u044f\\u043a\\u0438\\u0439 \\u0431\\u0443\\u0434\\u0443\\u0442\\u044c \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u043e\\u0432\\u0430\\u043d\\u0456 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/ImportTwo.txt",
    "content": "\\u0412\\u0438 \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0437\\u043c\\u0456\\u043d\\u0438\\u0442\\u0438 \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u043e\\u0432\\u0430\\u043d\\u0456 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457 \\u0442\\u0443\\u0442 \\u043f\\u0435\\u0440\\u0448 \\u043d\\u0456\\u0436 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0438 \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442.\r\n\r\n\\u0412\\u0438 \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u043f\\u0435\\u0440\\u0435\\u0433\\u043b\\u044f\\u043d\\u0443\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043e\\u043a \\u0434\\u043b\\u044f \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u0443 \\u0434\\u043b\\u044f \\u043a\\u0440\\u0430\\u0449\\u043e\\u0457 \\u043e\\u0440\\u0433\\u0430\\u043d\\u0456\\u0437\\u0430\\u0446\\u0456\\u0457 \\u0412\\u0430\\u0448\\u0438\\u0445 \\u0434\\u043e\\u0445\\u043e\\u0434\\u0456\\u0432 \\u0442\\u0430 \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442.\r\n\r\n\\u041d\\u043e\\u0432\\u0456 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457 \\u043c\\u043e\\u0436\\u0443\\u0442\\u044c \\u0431\\u0443\\u0442\\u0438 \\u0432\\u0438\\u0434\\u0430\\u043b\\u0435\\u043d\\u0456 \\u0430\\u0431\\u043e \\u0456\\u0457 \\u0441\\u0442\\u0430\\u043d \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u0443 \\u043c\\u043e\\u0436\\u0435\\u0442 \\u0431\\u0443\\u0442\\u0438 \\u0432\\u0438\\u043c\\u043a\\u043d\\u0435\\u043d\\u043e. \\u042f\\u043a\\u0449\\u043e \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u044e \\u0431\\u0443\\u0434\\u0435 \\u0432\\u0438\\u044f\\u0432\\u043b\\u0435\\u043d\\u043e, \\u044f\\u043a \\u0456\\u0441\\u043d\\u0443\\u044e\\u0447\\u0443, \\u0432\\u043e\\u043d\\u0430 \\u043c\\u043e\\u0436\\u0435 \\u0431\\u0443\\u0442\\u0438 \\u043f\\u0435\\u0440\\u0435\\u0437\\u0430\\u043f\\u0438\\u0441\\u0430\\u043d\\u0430 \\u0442\\u0430 \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442 \\u0431\\u0443\\u0434\\u0435 \\u0444\\u043e\\u0440\\u0441\\u043e\\u0432\\u0430\\u043d\\u043e.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/NewFileFour.txt",
    "content": "\\u0412\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044c \\u0433\\u0440\\u0443\\u043f\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0456\\u0432, \\u044f\\u043a\\u0456 \\u0432\\u0456\\u0434\\u043f\\u043e\\u0432\\u0456\\u0434\\u0430\\u044e\\u0442\\u044c \\u0412\\u0430\\u0448\\u0438\\u043c \\u043f\\u043e\\u0442\\u0440\\u0435\\u0431\\u0430\\u043c. \\u0412\\u0438 \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0434\\u043e\\u0434\\u0430\\u0442\\u0438, \\u0432\\u0438\\u0434\\u0430\\u043b\\u0438\\u0442\\u0438 \\u0430\\u0431\\u043e \\u0437\\u043c\\u0456\\u043d\\u0438\\u0442\\u0438 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0438 \\u043f\\u0456\\u0437\\u043d\\u0456\\u0448\\u0435.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/NewFileOne.txt",
    "content": "\\u0412\\u0432\\u0435\\u0434\\u0456\\u0442\\u044c \\u043d\\u0430\\u0437\\u0432\\u0443 \\u0431\\u0430\\u0437\\u0438 \\u0434\\u0430\\u043d\\u0438\\u0445, \\u044f\\u043a\\u0443 \\u0432\\u0438 \\u0437\\u0431\\u0438\\u0440\\u0430\\u0454\\u0442\\u0435\\u0441\\u044c \\u0432\\u0438\\u043a\\u043e\\u0440\\u0438\\u0441\\u0442\\u043e\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438.\r\n\r\n\\u0412 \\u0431\\u0456\\u043b\\u044c\\u0448\\u043e\\u0441\\u0442\\u0456 \\u0432\\u0438\\u043f\\u0430\\u0434\\u043a\\u0456\\u0432 \\u0432\\u0438 \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0437\\u0430\\u043b\\u0438\\u0448\\u0438\\u0442\\u0438 \\u0437\\u043d\\u0430\\u0447\\u0435\\u043d\\u043d\\u044f \\u0437\\u0430 \\u0437\\u0430\\u043c\\u043e\\u0432\\u0447\\u0435\\u043d\\u043d\\u044f\\u043c, \\u0430\\u043b\\u0435 \\u0434\\u043e\\u0441\\u0432\\u0456\\u0434\\u0447\\u0435\\u043d\\u0456 \\u043a\\u043e\\u0440\\u0438\\u0441\\u0442\\u0443\\u0432\\u0430\\u0447\\u0456 \\u043c\\u043e\\u0436\\u0443\\u0442\\u044c \\u0437\\u043c\\u0456\\u043d\\u0438\\u0442\\u0438 \\u0457\\u0445, \\u044f\\u043a\\u0449\\u043e \\u043d\\u0435\\u043e\\u0431\\u0445\\u0456\\u0434\\u043d\\u043e.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/NewFileThree.txt",
    "content": "\\u0422\\u0443\\u0442 \\u0432\\u0438 \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0432\\u0438\\u0431\\u0440\\u0430\\u0442\\u0438 \\u0432\\u0430\\u043b\\u044e\\u0442\\u0438, \\u0449\\u043e \\u0431\\u0443\\u0434\\u0443\\u0442\\u044c \\u0434\\u043e\\u0441\\u0442\\u0443\\u043f\\u043d\\u0456 \\u043f\\u0456\\u0434 \\u0447\\u0430\\u0441 \\u0441\\u0442\\u0432\\u043e\\u0440\\u0435\\u043d\\u043d\\u044f \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0456\\u0432. \\u042f\\u043a\\u0449\\u043e \\u0432\\u0438 \\u043d\\u0435 \\u0431\\u0443\\u0434\\u0435\\u0442\\u0435 \\u0432\\u0438\\u043a\\u043e\\u0440\\u0438\\u0441\\u0442\\u043e\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u043a\\u0456\\u043b\\u044c\\u043a\\u0430 \\u0432\\u0430\\u043b\\u044e\\u0442, \\u043d\\u0435 \\u0437\\u043c\\u0456\\u043d\\u044e\\u0439\\u0442\\u0435 \\u0446\\u044e \\u0444\\u043e\\u0440\\u043c\\u0443. \\u0412\\u0438 \\u0437\\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0434\\u043e\\u0434\\u0430\\u0442\\u0438 \\u0430\\u0431\\u043e \\u0432\\u0438\\u0434\\u0430\\u043b\\u0438\\u0442\\u0438 \\u0432\\u0430\\u043b\\u044e\\u0442\\u0438 \\u043f\\u0456\\u0437\\u043d\\u0456\\u0448\\u0435.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/NewFileTwo.txt",
    "content": "\\u0412\\u0430\\u043b\\u044e\\u0442\\u0430 \\u0437\\u0430 \\u0437\\u0430\\u043c\\u043e\\u0432\\u0447\\u0435\\u043d\\u043d\\u044f\\u043c -- \\u0446\\u0435 \\u0432\\u0430\\u043b\\u044e\\u0442\\u0430, \\u044f\\u043a\\u0443 \\u0412\\u0438 \\u0431\\u0443\\u0434\\u0435\\u0442\\u0435 \\u0432\\u0438\\u043a\\u043e\\u0440\\u0438\\u0441\\u0442\\u043e\\u0432\\u0443\\u0432\\u0430\\u0442\\u0438 \\u043d\\u0430\\u0439\\u0447\\u0430\\u0441\\u0442\\u0456\\u0448\\u0435.\r\n\\u0420\\u0430\\u0445\\u0443\\u043d\\u043a\\u0438 \\u0431\\u0443\\u0434\\u0443\\u0442\\u044c \\u0441\\u0442\\u0432\\u043e\\u0440\\u044e\\u0432\\u0430\\u0442\\u0438\\u0441\\u044f \\u0437\\u0430 \\u0437\\u0430\\u043c\\u043e\\u0432\\u0447\\u0435\\u043d\\u043d\\u044f\\u043c \\u0432 \\u0446\\u0456\\u0439 \\u0432\\u0430\\u043b\\u044e\\u0442\\u0456, \\u0430\\u043b\\u0435 \\u0432\\u0438 \\u0437\\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0432\\u0438\\u0431\\u0440\\u0430\\u0442\\u0438 \\u0439 \\u0456\\u043d\\u0448\\u0456 \\u0432\\u0430\\u043b\\u044e\\u0442\\u0438 \\u043f\\u0440\\u0438 \\u0441\\u0442\\u0432\\u043e\\u0440\\u0435\\u043d\\u043d\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0443.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/QifOne.txt",
    "content": "\\u0412 QIF-\\u0444\\u0430\\u0439\\u043b\\u0456, \\u0449\\u043e \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u0443\\u0454\\u0442\\u044c\\u0441\\u044f, \\u043d\\u0435 \\u0432\\u043a\\u0430\\u0437\\u0430\\u043d\\u0456 \\u043a\\u043e\\u0440\\u0435\\u0441\\u043f\\u043e\\u043d\\u0434\\u0443\\u044e\\u0447\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0438 (\\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0438 \\u043f\\u0440\\u0438\\u0437\\u043d\\u0430\\u0447\\u0435\\u043d\\u043d\\u044f).\r\n\\u0426\\u0435 \\u043d\\u043e\\u0440\\u043c\\u0430\\u043b\\u044c\\u043d\\u0430 \\u0441\\u0438\\u0442\\u0443\\u0430\\u0446\\u0456\\u044f \\u0434\\u043b\\u044f QIF-\\u0444\\u0430\\u0439\\u043b\\u0456\\u0432, \\u043e\\u0442\\u0440\\u0438\\u043c\\u0430\\u043d\\u043d\\u0438\\u0445 \\u0432\\u0456\\u0434 \\u0435\\u043b\\u0435\\u043a\\u0442\\u0440\\u043e\\u043d\\u043d\\u0438\\u0445 \\u0431\\u0430\\u043d\\u043a\\u0456\\u0432\\u0441\\u044c\\u043a\\u0438\\u0445 \\u0441\\u0438\\u0441\\u0442\\u0435\\u043c \\u0442\\u0430 \\u0456\\u043d\\u0448\\u0438\\u0449 \\u043f\\u043e\\u0434\\u0456\\u0431\\u043d\\u0438\\u0445 \\u0434\\u0436\\u0435\\u0440\\u0435\\u043b.\r\n\r\n\\u0411\\u0443\\u0434\\u044c \\u043b\\u0430\\u0441\\u043a\\u0430, \\u0432\\u0438\\u0431\\u0435\\u0440\\u0456\\u0442\\u044c \\u043a\\u043e\\u0440\\u0435\\u0441\\u043f\\u043e\\u043d\\u0434\\u0443\\u044e\\u0447\\u0438\\u0439 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043e\\u043a \\u0434\\u043b\\u044f \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0439.\r\n\\u041d\\u043e\\u0432\\u0456 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457 \\u0431\\u0443\\u0434\\u0443\\u0442\\u044c \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u044f\\u043c\\u0438 \\u0437 \\u043f\\u0440\\u043e\\u0441\\u0442\\u0438\\u043c \\u0437\\u0430\\u043f\\u0438\\u0441\\u043e\\u043c, \\u0442\\u0430\\u043a \\u044f\\u043a \\u0432 QIF-\\u0444\\u0430\\u0439\\u043b\\u0456 \\u043d\\u0435\\u0434\\u043e\\u0441\\u0442\\u0430\\u0442\\u043d\\u044c\\u043e \\u0456\\u043d\\u0444\\u043e\\u0440\\u043c\\u0430\\u0446\\u0456\\u0457 \\u0434\\u043b\\u044f \\u043f\\u043e\\u0434\\u0432\\u0456\\u0439\\u043d\\u043e\\u0433\\u043e \\u0437\\u0430\\u043f\\u0438\\u0441\\u0443.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/uk/QifTwo.txt",
    "content": "\\u0417\\u0430\\u0440\\u0430\\u0437 \\u0412\\u0438 \\u043c\\u043e\\u0436\\u0435\\u0442\\u0435 \\u0437\\u043c\\u0456\\u043d\\u0438\\u0442\\u0438 \\u043e\\u043f\\u0435\\u0440\\u0430\\u0446\\u0456\\u0457, \\u0449\\u043e \\u0456\\u043c\\u043f\\u043e\\u0440\\u0442\\u043e\\u0432\\u0430\\u043d\\u0456.\r\n\\u041c\\u043e\\u0436\\u043b\\u0438\\u0432\\u043e, \\u0412\\u0430\\u043c \\u0432\\u0430\\u0440\\u0442\\u043e \\u043f\\u0435\\u0440\\u0435\\u0432\\u0456\\u0440\\u0438\\u0442\\u0438 \\u043a\\u043e\\u0440\\u0435\\u0441\\u043f\\u043e\\u043d\\u0434\\u0443\\u044e\\u0447\\u0456 \\u0440\\u0430\\u0445\\u0443\\u043d\\u043a\\u0438, \\u0449\\u043e\\u0431 \\u043a\\u0440\\u0430\\u0449\\u0435 \\u0432\\u043f\\u043e\\u0440\\u044f\\u0434\\u043a\\u0443\\u0432\\u0430\\u0442\\u0438 \\u0432\\u0430\\u0448\\u0456 \\u0434\\u043e\\u0445\\u043e\\u0434\\u0438 \\u0442\\u0430 \\u0432\\u0438\\u0442\\u0440\\u0430\\u0442\\u0438.\r\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/ArchiveDate.txt",
    "content": "The cut off date determines where the split occurs between archived transactions and retained transactions.\n"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/ArchiveEquity.txt",
    "content": "A default Equity Account must be selected to transfer opening balances from.\n必须选中一个默认资产净值用于转入初始的余额。\nIf an acceptable Equity Account does not exist.  Exit the wizard and create a new Equity Account before continuing.\n如果不存在一个可用的资产净值账户，退出此向导并创建一个新的资产净值账户，再继续。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/ArchiveFile.txt",
    "content": "Choose a filename for the new file.  Do not choose your existing file.\n选择一个新文件名称。不要选择您已经存在的文件。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/CreateNewFile.txt",
    "content": "Would you like to create a new file?\r\n您希望创建 一个新文件？"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/DupeTransImport.txt",
    "content": "Duplicate transactions were found during the import.在导入过程中发现重复的交易记录。\r\nDo you want to save the duplicate transactions?您是否希望保存这些重复的记录？"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/FileNotSaved.txt",
    "content": "Changes have not been saved.还没有保存修改。\r\nDo you want to save?你是否要保存？"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/ImportFileZero.txt",
    "content": "Select the jGnash 1.11.x or newer file you wish to import.\r\n选择希望导入的jGnash 1.11.x或者更新的文件。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/ImportOne.txt",
    "content": "Choose a destination account for the imported transactions.\r\n为导入交易记录选择一个目标账户"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/ImportTwo.txt",
    "content": "You can modify the imported transactions here before completing the import.\n在完成导入之前，您可以在此修改导入的交易记录。\nYou may want to review the destination account to better organize your income and expenses.\n您也许希望核查目标账户，以便更好的组织您的收支。\nNew transactions can be deleted or their import state toggled off.  If a transaction is detected as pre-existing, it may be overridden and the import forced.\n新的交易记录可删除或者导入状态标为关闭。如果一条交易记录被检测到已经存在，那记录可能被覆盖并强制导入。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/NewBudgetOne.txt",
    "content": "This builds a new budget based on the prior years income and expenses.\r\n基于前一年的收支创建新的预算\r\nSelect the historical method of choice for creating a budget.\r\n选择创建预算的历史数据的选择方法\r\nBudget amounts may be refined at a later time if needed.\r\n如果需要，预算数量可以在稍后重新设置"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/NewFileFour.txt",
    "content": "Select the account groups that are relevant to your needs.  You can add, remove, and modify accounts as needed later.\n选择账户中需要的分组，随后您仍可以增加、删除、修改账户。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/NewFileOne.txt",
    "content": "Enter the the name of the database you would like to use.\r\n键入您希望使用的数据库名称。\r\nIn most cases, the defaults will be fine, but advanced users can change them if needed.\r\n在大多数情况下，默认设置可以工作良好，但对于资深用户可以按需修改。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/NewFileThree.txt",
    "content": "The currencies available for account creation can be selected below.  If you do not wish to use multiple currencies, do not change this form.\n在下面可以选择创建账户时可用的币种。如果你不愿意使用多币种，则无需做改变。\nCurrencies can be added and removed at any time.\n可以在任何时候添加或删除币种。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/NewFileTwo.txt",
    "content": "The default currency should be the currency you use most.  In most cases, only one currency will be used which will be your local currency.\r\n默认的币种应是您使最常用的货币种类。在大多数情况下，只有您所在地使用的货币是唯一的币种。\r\nAll accounts created within jGnash will use this currency by default, but other currencies can be selected when creating new accounts.\r\njGnash创建的所有账户都将只是用这一货币作为默认币种，但在创建新账户时也可以选择其他币种。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/QifOne.txt",
    "content": "The QIF file you have selected does not specify a destination account for the transactions. \nThis is normal for QIF files received from online banking and similar sources.\n您挑选QIF文件没有指定目的账户，这对从在线银行之类来源的QIF较为常见。\nSelect a destination account for the transactions.\nThe new transactions will be Single Entry transactions because of a lack of information supplied in the QIF file.\n选择一个目的账户存放交易条目。由于QIF缺乏相关信息，新的交易记录都将是单条目交易记录。"
  },
  {
    "path": "jgnash-resources/src/main/resources/jgnash/resource/text/zh/QifTwo.txt",
    "content": "You may change the QIF transactions here before completing the import.\n在完成导入前，您可以修改QIF交易\nYou may want to review the destination account to better organize your income and expenses.\n您也许希望检查目标的账户以便更好的安排收支"
  },
  {
    "path": "jgnash-tests/build.gradle.kts",
    "content": "description = \"jGnash Core Test Classes\"\n\nvar moduleName = \"jgnash.tests\"\n\nval nettyVersion: String by project\nval pdfBoxVersion: String by project\nval xstreamVersion: String by project\nval commonsMathVersion: String by project\nval commonsCollectionsVersion: String by project\nval commonsTextVersion: String by project\n\nval junitVersion: String by project\nval junitExtensionsVersion: String by project\nval awaitilityVersion: String by project\n\ndependencies {\n    testImplementation(\"org.junit.jupiter:junit-jupiter-api:$junitVersion\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter-params:$junitVersion\")\n    testRuntimeOnly(\"org.junit.jupiter:junit-jupiter-engine:$junitVersion\")\n\n    testImplementation(\"io.github.glytching:junit-extensions:$junitExtensionsVersion\")\n    testImplementation(\"org.awaitility:awaitility:$awaitilityVersion\")\n    testImplementation(\"org.apache.commons:commons-text:$commonsTextVersion\")\n\n    testImplementation(project(\":jgnash-resources\"))\n    testImplementation(project(\":jgnash-core\"))\n    testImplementation(project(\":jgnash-bayes\"))\n    testImplementation(project(\":jgnash-convert\"))\n    testImplementation(project(\":jgnash-report-core\"))\n\n    testImplementation(\"io.netty:netty-codec:$nettyVersion\")\n    testImplementation(\"com.thoughtworks.xstream:xstream:$xstreamVersion\")\n\n    testImplementation(\"org.apache.commons:commons-collections4:$commonsCollectionsVersion\")\n    testImplementation(\"org.apache.commons:commons-math3:$commonsMathVersion\")\n    testImplementation(\"org.apache.pdfbox:pdfbox:$pdfBoxVersion\")\n    testImplementation(\"org.apache.pdfbox:pdfbox-tools:$pdfBoxVersion\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n\n    // we want display the following test events\n    testLogging {\n        events(\"PASSED\", \"STARTED\", \"FAILED\", \"SKIPPED\")\n        showStandardStreams = true\n    }\n}\n\ntasks.jar {\n    manifest.attributes[\"Automatic-Module-Name\"] = moduleName\n}"
  },
  {
    "path": "jgnash-tests/src/test/java/ApiTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport jgnash.engine.AbstractEngineTest;\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineException;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionEntry;\nimport jgnash.engine.TransactionFactory;\nimport jgnash.engine.TransactionType;\nimport jgnash.util.LogUtil;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * Test methods for public API's.  These may duplicate some other tests but ensures intended API's remain publicly\n * accessible for plugins and UIs\n *\n * @author Craig Cavanaugh\n */\npublic class ApiTest extends AbstractEngineTest {\n\n    @BeforeAll\n    static void test() {\n        LogUtil.configureLogging();\n    }\n\n    @Override\n    protected Engine createEngine() throws IOException {\n        database = testFolder.createFile(\"api-test.bxds\").getAbsolutePath();\n\n        EngineFactory.deleteDatabase(database);\n\n        try {\n            final Engine engine =  EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.BINARY_XSTREAM);\n\n            engine.setCreateBackups(false); // disable for test\n\n            return engine;\n        } catch (final EngineException e) {\n            fail(\"Fatal error occurred\");\n            return  null;\n        }\n    }\n\n    private static void closeEngine() {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n    }\n\n    @Test\n    void testMove() {\n        Account test = new Account(AccountType.ASSET, e.getDefaultCurrency());\n        test.setName(\"Test\");\n\n        assertTrue(e.addAccount(e.getRootAccount(), test));\n        assertTrue(e.getRootAccount().contains(test));\n\n        assertTrue(e.moveAccount(test, usdBankAccount));\n        assertFalse(e.getRootAccount().contains(test));\n\n        assertTrue(usdBankAccount.contains(test));\n    }\n\n    @Test\n    void testPreferences() {\n        e.setPreference(\"myKey\", \"myValue\");\n        e.setPreference(\"myNumber\", BigDecimal.TEN.toString());\n\n        e.putBoolean(\"myBoolean\", true);\n        assertTrue(e.getBoolean(\"myBoolean\", false));\n\n        e.setRetainedBackupLimit(5);\n        assertEquals(5, e.getRetainedBackupLimit());\n\n        e.setRemoveOldBackups(false);\n        assertFalse(e.removeOldBackups());\n\n        e.setRemoveOldBackups(true);\n        assertTrue(e.removeOldBackups());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertEquals(\"myValue\", e.getPreference(\"myKey\"));\n        assertTrue(e.getBoolean(\"myBoolean\", false));\n        assertEquals(5, e.getRetainedBackupLimit());\n\n        final String number = e.getPreference(\"myNumber\");\n        assertNotNull(number);\n\n        assertEquals(BigDecimal.TEN, new BigDecimal(number));\n    }\n\n    @Test\n    void testSecurities() {\n        SecurityNode securityNode = new SecurityNode(e.getDefaultCurrency());\n        securityNode.setSymbol(\"GGG\");\n        securityNode.setScale((byte) 2);\n\n        e.addSecurity(securityNode);\n        assertEquals(securityNode, e.getSecurity(\"GGG\"));\n\n        assertEquals(0, securityNode.getHistoryNodes().size());\n        assertFalse(securityNode.getClosestHistoryNode(LocalDate.now()).isPresent());\n        assertFalse(securityNode.getHistoryNode(LocalDate.now()).isPresent());\n        assertEquals(BigDecimal.ZERO, securityNode.getMarketPrice(LocalDate.now(), e.getDefaultCurrency()));\n\n        // Add the security node to the account\n        final List<SecurityNode> securitiesList = new ArrayList<>(investAccount.getSecurities());\n        final int securityCount = securitiesList.size();\n        securitiesList.add(securityNode);\n        e.updateAccountSecurities(investAccount, securitiesList);\n        assertEquals(securityCount + 1, investAccount.getSecurities().size());\n\n        // Returned market price should be zero\n        assertEquals(BigDecimal.ZERO, Engine.getMarketPrice(new ArrayList<>(), securityNode, e.getDefaultCurrency(),\n                LocalDate.now()));\n\n        // Create and add some security history\n        final SecurityHistoryNode historyNode = new SecurityHistoryNode(LocalDate.now(), BigDecimal.TEN, 10000,\n                BigDecimal.TEN, BigDecimal.TEN);\n\n        e.addSecurityHistory(securityNode, historyNode);\n        assertEquals(1, securityNode.getHistoryNodes().size());\n\n        // Returned market price should be 10\n        assertEquals(BigDecimal.TEN, Engine.getMarketPrice(new ArrayList<>(), securityNode, e.getDefaultCurrency(),\n                LocalDate.now()));\n\n\n        // Ensure the security updates correctly with a replacement value\n        final SecurityHistoryNode historyNodeReplacement = new SecurityHistoryNode(LocalDate.now(), BigDecimal.ONE, 10000,\n                BigDecimal.ONE, BigDecimal.ONE);\n\n        e.addSecurityHistory(securityNode, historyNodeReplacement);\n        assertEquals(1, securityNode.getHistoryNodes().size());\n\n        // Returned market price should be 1\n        assertEquals(BigDecimal.ONE, Engine.getMarketPrice(new ArrayList<>(), securityNode, e.getDefaultCurrency(),\n                LocalDate.now()));\n    }\n\n    @Test\n    void testAccountAttributes() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(\"AccountAttributes\");\n\n        e.addAccount(e.getRootAccount(), a);\n        e.setAccountAttribute(a, \"myStuff\", \"gobbleDeGook\");\n        e.setAccountAttribute(a, \"myKey\", \"myValue\");\n        e.setAccountAttribute(a, \"myNumber\", BigDecimal.TEN.toString());\n\n        Account b = e.getAccountByUuid(a.getUuid());\n\n        assertEquals(\"gobbleDeGook\", Engine.getAccountAttribute(b, \"myStuff\"));\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        b = e.getAccountByUuid(a.getUuid());\n        assertEquals(\"gobbleDeGook\", Engine.getAccountAttribute(b, \"myStuff\"));\n        assertEquals(\"myValue\", Engine.getAccountAttribute(b, \"myKey\"));\n\n        String attribute = Engine.getAccountAttribute(b, \"myNumber\");\n\n        assertNotNull(attribute);\n\n        assertEquals(BigDecimal.TEN, new BigDecimal(attribute));\n    }\n\n    @Test\n    void testTransactionAPI() {\n        final String ACCOUNT_NAME = \"testAccount\";\n\n        final CurrencyNode node = e.getDefaultCurrency();\n\n        final Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        e.addAccount(e.getRootAccount(), a);\n\n        // Test single entry transaction\n        final Transaction transaction = TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(),\n                \"memo\", \"payee\", \"1\");\n\n        e.addTransaction(transaction);\n\n        assertTrue(a.contains(transaction));\n\n        assertEquals(0, a.indexOf(transaction));\n\n        assertEquals(TransactionType.SINGLENTRY, transaction.getTransactionType());\n\n        for (final TransactionEntry transactionEntry : transaction.getTransactionEntries()) {\n            assertFalse(transactionEntry.isMultiCurrency());\n        }\n    }\n\n    @Test\n    void placeHolder() {\n        assertTrue(e.getTransactionsWithAttachments().isEmpty());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/ClassPathTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash;\n\nimport java.util.Locale;\n\nimport jgnash.resource.util.ClassPathUtils;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n/**\n * Unit test for classpath utilities.\n *\n * @author Craig Cavanaugh\n */\nclass ClassPathTest {\n\n    ClassPathTest() {\n    }\n\n    //    @Test\n    //    public void findExtensions() {\n    //\n    //        Collection<String> list = ClassPathUtils.getFilesByExtension(\"/jgnash/resource\", \".xml\");\n    //\n    //        for (String string : list) {\n    //            System.out.println(string);\n    //        }\n    //\n    //        assertTrue(\"passed test: \", list.size() > 0);\n    //    }\n\n    @Test\n    void findRealPath() {\n\n        String path = ClassPathUtils.getLocalizedPath(\"/jgnash/resource/account\");\n\n        assertNotNull(path, \"failed test\");\n\n        System.out.println(path);\n    }\n\n    @Test\n    void findFakePath() {\n\n        Locale defaultLocale = Locale.getDefault();\n\n        Locale.setDefault(new Locale(\"ZZ\"));\n\n        String path = ClassPathUtils.getLocalizedPath(\"/jgnash/resource/account\");\n\n        Locale.setDefault(defaultLocale);\n\n        assertNotNull(path, \"failed test\");\n\n        System.out.println(path);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/JavascriptTest.js",
    "content": "load(\"nashorn:mozilla_compat.js\");  // Load compatibility script\n\nimportPackage(javax.swing);\nimportPackage(Packages.jgnash.ui);\nimportPackage(Packages.jgnash.engine);\n\nfunction debug(message) {   // helper function to print messages to the console\n    java.lang.System.out.println(message);\n}\n\nConsoleDialog.show();   // show the console dialog to see the debug information\n\nvar engine = EngineFactory.getEngine(EngineFactory.DEFAULT);    // this is how to get the default Engine instance\n\nvar accountList = engine.getAccountList();  // get a list of accounts\n\nfor (var i = 0; i < accountList.size(); i++)   // loop and print the account names to the console\n{\n    var account = accountList.get(i);\n    debug(account.toString());\n}\n\n// just to show how to use swing\nJOptionPane.showMessageDialog(null, 'Hello, world!');\n\n\n\n\n\n\n\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/PDFBoxTableTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash;\n\nimport java.awt.image.BufferedImage;\nimport java.awt.print.PageFormat;\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.util.Locale;\nimport java.util.ResourceBundle;\n\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DefaultCurrencies;\nimport jgnash.report.pdf.Report;\nimport jgnash.report.table.AbstractReportTableModel;\nimport jgnash.report.table.ColumnStyle;\nimport jgnash.report.table.GroupInfo;\nimport jgnash.report.ui.ReportPrintFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.time.DateUtils;\nimport jgnash.util.NotNull;\n\nimport org.apache.pdfbox.pdmodel.PDDocument;\nimport org.apache.pdfbox.pdmodel.PDPage;\nimport org.apache.pdfbox.pdmodel.PDPageContentStream;\nimport org.apache.pdfbox.pdmodel.font.PDFont;\nimport org.apache.pdfbox.pdmodel.font.PDType1Font;\nimport org.apache.pdfbox.tools.imageio.ImageIOUtil;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass PDFBoxTableTest {\n\n    @BeforeEach\n    void setUp() {\n        Locale.setDefault(Locale.US);\n        DateUtils.setShortDateFormatPattern(\"MM/dd/yy\");\n    }\n\n    @Test\n    void simpleTest() throws IOException {\n\n        Path tempPath = null;\n\n        try (PDDocument doc = new PDDocument()) {\n            tempPath = Files.createTempFile(\"test\", \".pdf\");\n            System.out.println(tempPath);\n\n            PDPage page = new PDPage();\n            doc.addPage(page);\n\n            PDFont font = PDType1Font.HELVETICA;\n\n            try (final PDPageContentStream contents = new PDPageContentStream(doc, page)) {\n                contents.beginText();\n                contents.setFont(font, 11);\n                contents.newLineAtOffset(100, 700);\n                contents.showText(\"Hello World!\");\n                contents.endText();\n            }\n\n            doc.save(tempPath.toFile());\n        } catch (final IOException e) {\n            e.printStackTrace();\n        } finally {\n            if (tempPath != null) {\n                Files.deleteIfExists(tempPath);\n            }\n        }\n    }\n\n    private static class SimpleReport extends Report {\n\n    }\n\n    @Test\n    void basicReportTest() throws IOException {\n\n        Path tempPath = null;\n        Path tempRasterPath = null;\n\n        try (final Report report = new SimpleReport()) {\n            tempPath = Files.createTempFile(\"pdfTest\", \".pdf\");\n            tempRasterPath = Files.createTempFile(\"pdfTest\", \".png\");\n\n            final float padding = 2.5f;\n\n            report.setTableFont(PDType1Font.COURIER);\n            report.setHeaderFont(PDType1Font.HELVETICA_BOLD);\n            report.setCellPadding(padding);\n            report.setBaseFontSize(9);\n\n            final PageFormat pageFormat = ReportPrintFactory.getDefaultPage();\n            pageFormat.setOrientation(PageFormat.LANDSCAPE);\n            report.setPageFormat(pageFormat);\n\n            report.setFooterFont(PDType1Font.TIMES_ITALIC);\n            report.setEllipsis(\"…\");\n\n            assertEquals(1, GroupInfo.getGroups(new BasicTestReport()).size());\n            //assertEquals(80, ((GroupInfo) GroupInfo.getGroups(new BasicTestReport()).toArray()[0]).rows);\n\n            report.addTable(new BasicTestReport());\n            report.addFooter();\n\n            report.saveToFile(tempPath);\n\n            assertTrue(report.isLandscape());\n            assertEquals(padding, report.getCellPadding());\n            assertTrue(Files.exists(tempPath));\n\n\n            // Create a PNG file\n            final BufferedImage bim = report.renderImage(0, 300);\n            ImageIOUtil.writeImage(bim, tempRasterPath.toString(), 300);\n\n            assertTrue(Files.exists(tempRasterPath));\n\n        } catch (final IOException e) {\n            e.printStackTrace();\n        } finally {\n            if (tempPath != null) {\n                Files.deleteIfExists(tempPath);\n            }\n\n            if (tempRasterPath != null) {\n                Files.deleteIfExists(tempRasterPath);\n            }\n        }\n    }\n\n    @Test\n    void crossTabReportTest() throws IOException {\n\n        Path tempPath = null;\n        Path tempRasterPath = null;\n\n        try(final Report report = new SimpleReport()) {\n            tempPath = Files.createTempFile(\"pdfTest\", \".pdf\");\n            tempRasterPath = Files.createTempFile(\"pdfTest\", \".png\");\n\n            final float padding = 2.5f;\n\n            report.setTableFont(PDType1Font.COURIER);\n            report.setHeaderFont(PDType1Font.HELVETICA_BOLD);\n            report.setCellPadding(padding);\n            report.setBaseFontSize(9);\n            report.setForceGroupPagination(true);\n\n            final PageFormat pageFormat = (PageFormat) report.getPageFormat().clone();\n            pageFormat.setOrientation(PageFormat.LANDSCAPE);\n            report.setPageFormat(pageFormat);\n\n            report.setFooterFont(PDType1Font.TIMES_ITALIC);\n            report.setEllipsis(\"…\");\n\n            assertEquals(2, GroupInfo.getGroups(new CrossTabTestReport()).size());\n\n            report.addTable(new CrossTabTestReport());\n            report.addFooter();\n\n            report.saveToFile(tempPath);\n\n            assertTrue(report.isLandscape());\n            assertEquals(padding, report.getCellPadding());\n            assertTrue(Files.exists(tempPath));\n\n            // Create a PNG file\n            final BufferedImage bim = report.renderImage(0, 300);\n            ImageIOUtil.writeImage(bim, tempRasterPath.toString(), 300);\n\n            assertTrue(Files.exists(tempRasterPath));\n\n        } catch (final IOException e) {\n            e.printStackTrace();\n        } finally {\n            if (tempPath != null) {\n                Files.deleteIfExists(tempPath);\n            }\n\n            if (tempRasterPath != null) {\n                Files.deleteIfExists(tempRasterPath);\n            }\n        }\n    }\n\n    private static class BasicTestReport extends AbstractReportTableModel {\n\n        private static final String COLUMN_DATE = \"Column.Date\";\n        private static final String COLUMN_NUM = \"Column.Num\";\n        private static final String COLUMN_PAYEE = \"Column.Payee\";\n        private static final String COLUMN_MEMO = \"Column.Memo\";\n        private static final String COLUMN_ACCOUNT = \"Column.Account\";\n        private static final String COLUMN_CLR = \"Column.Clr\";\n        private static final String COLUMN_DEPOSIT = \"Column.Deposit\";\n        private static final String COLUMN_WITHDRAWAL = \"Column.Withdrawal\";\n        private static final String COLUMN_BALANCE = \"Column.Balance\";\n        private static final String COLUMN_TIMESTAMP = \"Column.Timestamp\";\n\n        private final ResourceBundle rb = ResourceUtils.getBundle();\n\n        private final ColumnStyle[] columnStyles = {ColumnStyle.SHORT_DATE, ColumnStyle.TIMESTAMP,\n                ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING,\n                ColumnStyle.SHORT_AMOUNT, ColumnStyle.SHORT_AMOUNT, ColumnStyle.AMOUNT_SUM};\n\n        private final String[] columnNames = {rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP),\n                rb.getString(COLUMN_NUM), rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO),\n                rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR), rb.getString(COLUMN_DEPOSIT),\n                rb.getString(COLUMN_WITHDRAWAL), rb.getString(COLUMN_BALANCE)};\n\n        final CurrencyNode currencyNode = DefaultCurrencies.buildCustomNode(\"USD\");\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return currencyNode;\n        }\n\n        @Override\n        public ColumnStyle getColumnStyle(int columnIndex) {\n            return columnStyles[columnIndex];\n        }\n\n        @Override\n        public Class<?> getColumnClass(final int columnIndex) {\n\n            switch (columnIndex) {\n                case 0:\n                    return LocalDate.class;\n                case 1:\n                    return LocalDateTime.class;\n                case 2:\n                case 3:\n                case 4:\n                case 5:\n                case 6:\n                    return String.class;\n                default:\n                    return BigDecimal.class;\n            }\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            switch (columnIndex) {\n                case 0:\n                case 1:\n                case 2:\n                case 6:\n                case 7:\n                case 8:\n                case 9:\n                    return true;\n                default:\n                    return false;\n            }\n        }\n\n        @Override\n        @NotNull\n        public String getColumnName(final int columnIndex) {\n            return columnNames[columnIndex];\n        }\n\n        @Override\n        public int getRowCount() {\n            return 80;\n        }\n\n        @Override\n        public int getColumnCount() {\n            return columnNames.length;\n        }\n\n        @Override\n        public Object getValueAt(int rowIndex, int columnIndex) {\n\n            switch (columnIndex) {\n                case 0:\n                    return LocalDate.now().plusDays(rowIndex);\n                case 1:\n                    return LocalDateTime.now().plusDays(rowIndex);\n                case 2:\n                    return Integer.toString(1000 + rowIndex);\n                case 3:\n                    return \"Payee \" + rowIndex;\n                case 4:\n                    return \"A Typical Memo \" + rowIndex;\n                case 5:\n                    return \"Account \" + rowIndex;\n                case 6:\n                    return \"R\";\n                case 7:\n                    return rowIndex % 2 == 0 ? new BigDecimal(100 + rowIndex) : null;\n                case 8:\n                    return rowIndex % 2 != 0 ? new BigDecimal(100 + rowIndex) : null;\n                case 9:\n                    return new BigDecimal(1000 + rowIndex * 10);\n                default:\n                    return null;\n            }\n        }\n\n        public int[] getColumnsToHide() {\n            // return new int[] {1};   // hide the timestamp\n            return new int[0];  // return an empty array by default\n        }\n\n        @Override\n        public String getTitle() {\n            return \"Test Report\";\n        }\n\n        @Override\n        public String getSubTitle() {\n            return null;\n        }\n    }\n\n    private static class CrossTabTestReport extends AbstractReportTableModel {\n\n        private static final String COLUMN_DATE = \"Column.Date\";\n        private static final String COLUMN_NUM = \"Column.Num\";\n        private static final String COLUMN_PAYEE = \"Column.Payee\";\n        private static final String COLUMN_MEMO = \"Column.Memo\";\n        private static final String COLUMN_ACCOUNT = \"Column.Account\";\n        private static final String COLUMN_CLR = \"Column.Clr\";\n        private static final String COLUMN_DEPOSIT = \"Column.Deposit\";\n        private static final String COLUMN_WITHDRAWAL = \"Column.Withdrawal\";\n        private static final String COLUMN_BALANCE = \"Column.Balance\";\n        private static final String COLUMN_TIMESTAMP = \"Column.Timestamp\";\n\n        private final ResourceBundle rb = ResourceUtils.getBundle();\n\n        private final ColumnStyle[] columnStyles = {ColumnStyle.SHORT_DATE, ColumnStyle.TIMESTAMP,\n                ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING, ColumnStyle.STRING,\n                ColumnStyle.SHORT_AMOUNT, ColumnStyle.SHORT_AMOUNT, ColumnStyle.AMOUNT_SUM, ColumnStyle.GROUP};\n\n        private final String[] columnNames = {rb.getString(COLUMN_DATE), rb.getString(COLUMN_TIMESTAMP),\n                rb.getString(COLUMN_NUM), rb.getString(COLUMN_PAYEE), rb.getString(COLUMN_MEMO),\n                rb.getString(COLUMN_ACCOUNT), rb.getString(COLUMN_CLR), rb.getString(COLUMN_DEPOSIT),\n                rb.getString(COLUMN_WITHDRAWAL), rb.getString(COLUMN_BALANCE), \"group\"};\n\n        final CurrencyNode currencyNode = DefaultCurrencies.buildCustomNode(\"USD\");\n\n        @Override\n        public CurrencyNode getCurrencyNode() {\n            return currencyNode;\n        }\n\n        @Override\n        public ColumnStyle getColumnStyle(int columnIndex) {\n            return columnStyles[columnIndex];\n        }\n\n        @Override\n        public Class<?> getColumnClass(final int columnIndex) {\n\n            switch (columnIndex) {\n                case 0:\n                    return LocalDate.class;\n                case 1:\n                    return LocalDateTime.class;\n                case 2:\n                case 3:\n                case 4:\n                case 5:\n                case 6:\n                case 10:\n                    return String.class;\n                default:\n                    return BigDecimal.class;\n            }\n        }\n\n        @Override\n        public boolean isColumnFixedWidth(final int columnIndex) {\n            switch (columnIndex) {\n                case 0:\n                case 1:\n                case 2:\n                case 6:\n                case 7:\n                case 8:\n                case 9:\n                case 10:\n                    return true;\n                default:\n                    return false;\n            }\n        }\n\n        @Override\n        @NotNull\n        public String getColumnName(final int columnIndex) {\n            return columnNames[columnIndex];\n        }\n\n        @Override\n        public int getRowCount() {\n            return 80;\n        }\n\n        @Override\n        public int getColumnCount() {\n            return columnNames.length;\n        }\n\n        @Override\n        public Object getValueAt(int rowIndex, int columnIndex) {\n\n            if (rowIndex > getRowCount() - 1) {\n                throw new IndexOutOfBoundsException();\n            }\n\n            switch (columnIndex) {\n                case 0:\n                    return LocalDate.now().plusDays(rowIndex);\n                case 1:\n                    return LocalDateTime.now().plusDays(rowIndex);\n                case 2:\n                    return Integer.toString(1000 + rowIndex);\n                case 3:\n                    return \"Payee \" + rowIndex;\n                case 4:\n                    return \"A Typical Memo \" + rowIndex;\n                case 5:\n                    return \"Account \" + rowIndex;\n                case 6:\n                    return \"R\";\n                case 7:\n                    return rowIndex % 2 == 0 ? new BigDecimal(100 + rowIndex) : null;\n                case 8:\n                    return rowIndex % 2 != 0 ? new BigDecimal(100 + rowIndex) : null;\n                case 9:\n                    return new BigDecimal(1000 + rowIndex * 10);\n                case 10:\n                    return rowIndex % 2 == 0 ? \"Income\" : \"Expense\";\n                default:\n                    return null;\n            }\n        }\n\n        public int[] getColumnsToHide() {\n            // return new int[] {1};   // hide the timestamp\n            return new int[0];  // return an empty array by default\n        }\n\n        @Override\n        public String getTitle() {\n            return \"Test Report\";\n        }\n\n        @Override\n        public String getSubTitle() {\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/VersionTest.java",
    "content": "package jgnash;\n\nimport jgnash.resource.util.OS;\nimport jgnash.resource.util.Version;\n\nimport org.apache.commons.math3.util.Precision;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport static jgnash.resource.util.OS.JAVA_VERSION;\nimport static jgnash.resource.util.Version.getAppVersion;\n\n/**\n * Test methods\n *\n * @author Craig Cavanaugh\n */\nclass VersionTest {\n\n    @Test\n    void gitHub() {\n        assertTrue(Version.getLatestGitHubRelease().isPresent());\n    }\n\n    @Test\n    void testVersion() {\n        System.out.println(getAppVersion());\n\n        assertFalse(Version.isReleaseCurrent(\"2.20\"));\n        assertFalse(Version.isReleaseCurrent(\"2.20.0\"));\n        assertTrue(Version.isReleaseCurrent(\"10.40.40\"));\n    }\n\n    @Test\n    void testJavaVersion() {\n\n        System.out.println(\"Java Version: \" + OS.getJavaVersion());\n\n        assertTrue(OS.getJavaVersion() > 0);\n    }\n\n    @Test\n    void testJavaRelease() {\n\n        System.out.println(\"Java Version: \" + OS.getJavaVersion());\n\n        if (Precision.equals(OS.getJavaVersion(), 1.8f)) {  // don't test if not on Java 8\n            System.out.println(\"Java Release: \" + OS.getJavaRelease());\n            assertTrue(OS.getJavaRelease() > 0);\n\n            // test for early access versions\n            System.setProperty(JAVA_VERSION, \"1.8.0_202-ea\");\n            assertEquals(202, OS.getJavaRelease());\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/bayes/BayesClassifierTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.bayes;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass BayesClassifierTest {\n\n    @Test\n    void testClassifier() {\n        BayesClassifier<String> classifier = new BayesClassifier<>(\"default\");\n\n        classifier.train(\"Gasoline oil washer fluid brakes lights transmission auto\", \"Auto\");\n        classifier.train(\"groceries bacon fish burger milk chips\", \"Grocery\");\n        classifier.train(\"movie video DVD music theater\", \"Entertainment\");\n\n        assertEquals(\"Auto\", classifier.classify(\"Oil and washer fluid\"));\n        assertEquals(\"Grocery\", classifier.classify(\"Fish and chips\"));\n        assertEquals(\"default\",  classifier.classify(\"flowers and shrubs\"));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/convert/importat/FilterTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author Craig Cavanaugh\n */\n\nclass FilterTest {\n\n    @Test\n    void simpleTest() {\n\n        ImportFilter importFilter = new ImportFilter(\"/jgnash/convert/scripts/tidy.js\");\n\n        ImportTransaction importTransaction = new ImportTransaction();\n        importTransaction.setMemo(\"test\");\n\n        importFilter.acceptTransaction(importTransaction);\n\n        assertEquals(\"Fuzzy Wuzzy\", importFilter.processPayee(\"FuZZy WUzzY\"));\n        assertEquals(\"Hair cut\", importFilter.processMemo(\"HaIR cUT\"));\n        assertEquals(\"Tidy Memo and Payee fields\", importFilter.getDescription());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/convert/importat/ofx/Ofx2Test.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.math.BigDecimal;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.List;\n\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.util.FileMagic;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\nimport static jgnash.util.LogUtil.logSevere;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * JUnit 5 test class\n * \n * @author Craig Cavanaugh\n */\nclass Ofx2Test {\n\n    private static OfxV2Parser parser;\n\n    @BeforeAll\n    static void setUp() {\n        OfxV2Parser.enableDetailedLogFile();  // enable debugging\n        parser = new OfxV2Parser();\n    }\n\n    @Test\n    void parseSpec201() {\n                \n        try (InputStream stream =  Ofx2Test.class.getResourceAsStream(\"/ofx_spec201_stmtrs_example.xml\")) {\n            parser.parse(stream);\n\n            assertEquals(\"ENG\", parser.getLanguage());\n            assertEquals(\"INFO\", parser.getStatusSeverity());\n            assertEquals(0, parser.getStatusCode());\n\n            final OfxBank bank = parser.getBank();\n            assertNotNull(bank);\n\n            assertEquals(0, bank.statusCode);\n            assertEquals(\"INFO\", bank.statusSeverity);\n\n            assertFalse(parser.getBank().isInvestmentAccount());\n        } catch (IOException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseActivity() {\n        final String testFile = \"/activity.ofx\";\n\n        final URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                final OfxBank bank = parser.getBank();\n                assertNotNull(bank);\n\n                assertEquals(0, bank.statusCode);\n                assertEquals(\"INFO\", bank.statusSeverity);\n                assertEquals(\"Success\", bank.statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseBankOne() {\n        final String testFile = \"/bank1.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertNull(parser.getBank().statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseBankTwo() {\n        final String testFile = \"/bank2.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertNull(parser.getBank().statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n\n    /**\n     * Test for amounts that use a comma as a decimal SEPARATOR\n     */\n    @Test\n    void parseBankOneCommas() {\n        final String testFile = \"/bank1-commas.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertNull(parser.getBank().statusMessage);\n\n                assertEquals(new BigDecimal(\"524.10\"), parser.getBank().ledgerBalance);\n                assertEquals(new BigDecimal(\"519.10\"), parser.getBank().availBalance);\n\n                List<ImportTransaction> transactions = parser.getBank().getTransactions();\n\n                assertEquals(new BigDecimal(\"-130.00\"), transactions.get(0).getAmount());\n                assertEquals(new BigDecimal(\"-120.00\"), transactions.get(1).getAmount());\n                assertEquals(new BigDecimal(\"300.01\"), transactions.get(2).getAmount());\n                assertEquals(new BigDecimal(\"160.50\"), transactions.get(3).getAmount());\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseCheckingOne() {\n        final String testFile = \"/checking1.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertNull(parser.getBank().statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseChequing() {\n\n        final String testFile = \"/chequing.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertEquals(\"OK\", parser.getBank().statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n           fail();\n        }\n    }\n\n    @Test\n    void parseComptes() {\n\n        final String testFile = \"/comptes.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"FRA\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertNull(parser.getBank().statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseDemobank() {\n\n        final String testFile = \"/demobank.ofx\";\n\n        try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n            parser.parse(OfxV1ToV2.convertToXML(stream), \"ISO-8859-1\");\n\n            assertEquals(\"JPN\", parser.getLanguage());\n            assertEquals(\"INFO\", parser.getStatusSeverity());\n            assertEquals(0, parser.getStatusCode());\n\n            assertEquals(0, parser.getBank().statusCode);\n            assertEquals(\"INFO\", parser.getBank().statusSeverity);\n            assertNull(parser.getBank().statusMessage);\n\n            assertFalse(parser.getBank().isInvestmentAccount());\n        } catch (IOException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseSample() {\n        final String testFile = \"/Sample.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            assertTrue(FileMagic.isOfxV1(Paths.get(url.toURI())));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"JPN\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertNull(parser.getBank().statusMessage);\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseFileWithAccents() {\n        final String testFile = \"/File_with_Accents.ofx\";\n\n        URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"FRA\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, parser.getBank().statusCode);\n                assertEquals(\"INFO\", parser.getBank().statusSeverity);\n                assertEquals(\"OK\", parser.getBank().statusMessage);\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n\n    }\n\n    @Test\n    void parseInvest() {\n        final String testFile = \"/invest.xml\";\n\n        final URL url = Ofx2Test.class.getResource(testFile);\n\n        try {\n            assertTrue(FileMagic.isOfxV2(Paths.get(url.toURI())));\n\n            try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(stream);\n\n                OfxBank ofxBank = parser.getBank();\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, ofxBank.statusCode);\n                assertEquals(\"INFO\", ofxBank.statusSeverity);\n                assertNull(ofxBank.statusMessage);\n\n                assertEquals(2, ofxBank.getTransactions().size());\n                assertEquals(3, ofxBank.getSecurityList().size());\n\n                assertTrue(ofxBank.isInvestmentAccount());\n            } catch (final IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (final URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parse401k() {\n        final String testFile = \"/401k.xml\";\n\n        final URL url = Ofx2Test.class.getResource(testFile);\n\n        try {\n            assertTrue(FileMagic.isOfxV2(Paths.get(url.toURI())));\n\n            try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(stream);\n\n                OfxBank ofxBank = parser.getBank();\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, ofxBank.statusCode);\n                assertEquals(\"INFO\", ofxBank.statusSeverity);\n                assertNull(ofxBank.statusMessage);\n\n                assertEquals(3, ofxBank.getTransactions().size());\n                assertEquals(3, ofxBank.getSecurityList().size());\n\n\n                assertTrue(ofxBank.isInvestmentAccount());\n            } catch (final IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (final URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parse401kWithHeader() {\n        final String testFile = \"/401k-header.xml\";\n\n        final URL url = Ofx2Test.class.getResource(testFile);\n\n        try {\n            assertTrue(FileMagic.isOfxV2(Paths.get(url.toURI())));\n\n            try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(stream);\n\n                OfxBank ofxBank = parser.getBank();\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertEquals(0, ofxBank.statusCode);\n                assertEquals(\"INFO\", ofxBank.statusSeverity);\n                assertNull(ofxBank.statusMessage);\n\n                assertEquals(3, ofxBank.getTransactions().size());\n                assertEquals(3, ofxBank.getSecurityList().size());\n\n\n                assertTrue(ofxBank.isInvestmentAccount());\n            } catch (final IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (final URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseInvest2() {\n        final String testFile = \"/invest2.xml\";\n\n        final URL url = Ofx2Test.class.getResource(testFile);\n\n        try {\n            assertTrue(FileMagic.isOfxV2(Paths.get(url.toURI())));\n\n            try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(stream);\n\n                OfxBank ofxBank = parser.getBank();\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n                assertEquals(\"The operation succeeded.\", parser.getStatusMessage());\n\n                assertEquals(0, ofxBank.statusCode);\n                assertEquals(\"INFO\", ofxBank.statusSeverity);\n                assertNull(ofxBank.statusMessage);\n\n                assertEquals(60, ofxBank.getTransactions().size());\n                assertEquals(6, ofxBank.getSecurityList().size());\n\n                assertTrue(ofxBank.isInvestmentAccount());\n            } catch (final IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (final URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseUglyFile() throws Exception {\n        final String testFile = \"/uglyFormat.ofx\";\n\n        final Path path = Paths.get(Ofx2Test.class.getResource(testFile).toURI());\n\n        assertTrue(Files.exists(path));\n\n        assertTrue(FileMagic.isOfxV1(path));\n\n        try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n            String result = OfxV1ToV2.convertToXML(stream);\n            System.out.println(result);\n        } catch (final IOException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n\n        final URL url = Ofx2Test.class.getResource(testFile);\n        String encoding;\n\n        try {\n            encoding = FileMagic.getOfxV1Encoding(Paths.get(url.toURI()));\n\n            try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n\n                parser.parse(OfxV1ToV2.convertToXML(stream), encoding);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n                assertEquals(0, parser.getStatusCode());\n\n                assertFalse(parser.getBank().isInvestmentAccount());\n\n                assertEquals(5, parser.getBank().getTransactions().size());\n\n                // System.out.println(parser.getBank().getTransactions().get(4).getPayee());\n\n                assertTrue(parser.getBank().getTransactions().get(4).getPayee().contains(\"& &\"));\n\n                assertTrue(parser.getBank().getTransactions().get(4).getPayee().contains(\"\\\"\"));\n\n                assertTrue(parser.getBank().getTransactions().get(4).getPayee().contains(\"'\"));\n\n                assertTrue(parser.getBank().getTransactions().get(4).getPayee().contains(\"Am'ount\"));\n\n                assertFalse(parser.getBank().getTransactions().get(0).getPayee().contains(\"&\"));\n            } catch (IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (final URISyntaxException e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n    @Test\n    void parseUglyFile2() throws Exception {\n        final String testFile = \"/test_fails.ofx\";\n\n        final Path path = Paths.get(Ofx2Test.class.getResource(testFile).toURI());\n\n        assertTrue(Files.exists(path));\n\n        assertTrue(FileMagic.isOfxV2(path));\n\n        try {\n\n            try (final InputStream stream = Ofx2Test.class.getResourceAsStream(testFile)) {\n                parser.parse(stream);\n\n                OfxBank ofxBank = parser.getBank();\n\n                assertNotNull(ofxBank);\n\n                assertEquals(\"ENG\", parser.getLanguage());\n                assertEquals(0, parser.getStatusCode());\n                assertEquals(\"INFO\", parser.getStatusSeverity());\n\n                assertNull(ofxBank.statusMessage);\n\n                assertEquals(2, ofxBank.getTransactions().size());\n\n                assertFalse(ofxBank.isInvestmentAccount());\n            } catch (final IOException e) {\n                logSevere(Ofx2Test.class, e);\n                fail();\n            }\n        } catch (final Exception e) {\n            logSevere(Ofx2Test.class, e);\n            fail();\n        }\n    }\n\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/convert/importat/ofx/OfxConvertTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * JUnit 4 test class\n * \n * @author Craig Cavanaugh\n */\n\nclass OfxConvertTest {\n\n    @Test\n    void parseBankOne() {\n        \n        try (InputStream stream = OfxConvertTest.class.getResourceAsStream(\"/bank1.ofx\")) {\n            System.out.println(OfxV1ToV2.convertToXML(stream));\n        } catch (final IOException e) {\n            Logger.getLogger(OfxConvertTest.class.getName()).log(Level.SEVERE, null, e);\n            fail();\n        }\n       \n        assertTrue(true);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/convert/importat/ofx/OfxExportText.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.convert.importat.ofx;\n\nimport jgnash.convert.exportantur.ofx.OfxExport;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.engine.AbstractEngineTest;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.Transaction;\nimport jgnash.engine.TransactionFactory;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.LocalDate;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\npublic class OfxExportText extends AbstractEngineTest {\n\n    @Override\n    protected Engine createEngine() throws IOException {\n        database = testFolder.createFile(\"ofxExportTest.xml\").getAbsolutePath();\n\n        EngineFactory.deleteDatabase(database);\n\n        return EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.XML);\n    }\n\n    @Test\n    void testExport() throws Exception {\n\n        Transaction transaction = TransactionFactory.generateDoubleEntryTransaction(checkingAccount, usdBankAccount,\n                BigDecimal.TEN, LocalDate.now(), \"Transfer Test\", \"Transfer\", \"\");\n\n        assertTrue(e.addTransaction(transaction));\n\n        Path path = Files.createTempFile(\"j\", \".ofx\");\n\n        OfxExport ofxExport = new OfxExport(usdBankAccount, LocalDate.now().minusDays(1), LocalDate.now().plusDays(1),\n                path.toFile());\n\n        ofxExport.exportAccount();\n\n        assertTrue(Files.exists(path));\n\n        OfxBank ofxBank = OfxV2Parser.parse(path);\n\n        assertNotNull(ofxBank);\n\n        assertEquals(0, ofxBank.statusCode);\n        assertEquals(\"INFO\", ofxBank.statusSeverity);\n\n        assertEquals(1, ofxBank.getTransactions().size());\n\n        ImportTransaction importTransaction = ofxBank.getTransactions().get(0);\n\n        assertEquals(\"10001-C01\", importTransaction.getAccountTo());\n\n        Files.delete(path);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/convert/importat/qif/QifUtilsTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.convert.importat.qif;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * @author Craig Cavanaugh\n *\n */\nclass QifUtilsTest {\n\n    QifUtilsTest() {\n    }\n\n    @Test\n    void testTagStrip() {\n        // Auto:Gas/matrix:Vacation > Auto:Gas\n        assertEquals(\"Auto:Gas\", QifUtils.stripCategoryTags(\"Auto:Gas/matrix:Vacation\"));\n    }\n}"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/AbstractEngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport io.github.glytching.junit.extension.folder.TemporaryFolder;\nimport io.github.glytching.junit.extension.folder.TemporaryFolderExtension;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * Abstract base for testing the core engine API's.\n *\n * @author Craig Cavanaugh\n */\n@ExtendWith(TemporaryFolderExtension.class)\npublic abstract class AbstractEngineTest {\n\n    protected TemporaryFolder testFolder;\n\n    protected String database;\n\n    protected Engine e;\n\n    Account incomeAccount;\n\n    Account expenseAccount;\n\n    protected Account usdBankAccount;\n\n    protected Account checkingAccount;\n\n    Account equityAccount;\n\n    protected Account investAccount;\n\n    SecurityNode gggSecurityNode;\n\n    protected abstract Engine createEngine() throws IOException;\n\n    @BeforeEach\n    public void setUp(final TemporaryFolder testFolder) throws Exception {\n        Locale.setDefault(Locale.US);\n\n        this.testFolder = testFolder;\n\n        e = createEngine();\n\n        assertNotNull(e);\n\n        e.setCreateBackups(false);\n\n        // Creating currencies\n        CurrencyNode defaultCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n        e.addCurrency(defaultCurrency);\n        e.setDefaultCurrency(defaultCurrency);\n\n        CurrencyNode cadCurrency = DefaultCurrencies.buildCustomNode(\"CAD\");\n        e.addCurrency(cadCurrency);\n\n        // Creating accounts\n        incomeAccount = new Account(AccountType.INCOME, defaultCurrency);\n        incomeAccount.setName(\"Income Account\");\n        e.addAccount(e.getRootAccount(), incomeAccount);\n\n        expenseAccount = new Account(AccountType.EXPENSE, defaultCurrency);\n        expenseAccount.setName(\"Expense Account\");\n        e.addAccount(e.getRootAccount(), expenseAccount);\n\n        usdBankAccount = new Account(AccountType.BANK, defaultCurrency);\n        usdBankAccount.setName(\"USD Bank Account\");\n        usdBankAccount.setBankId(\"xyzabc\");\n        usdBankAccount.setAccountNumber(\"10001-A01\");\n        e.addAccount(e.getRootAccount(), usdBankAccount);\n\n        checkingAccount = new Account(AccountType.CHECKING, defaultCurrency);\n        checkingAccount.setName(\"Checking Account\");\n        checkingAccount.setBankId(\"xyzabc\");\n        checkingAccount.setAccountNumber(\"10001-C01\");\n        e.addAccount(e.getRootAccount(), checkingAccount);\n\n        Account cadBankAccount = new Account(AccountType.BANK, cadCurrency);\n        cadBankAccount.setName(\"CAD Bank Account\");\n        e.addAccount(e.getRootAccount(), cadBankAccount);\n\n        equityAccount = new Account(AccountType.EQUITY, defaultCurrency);\n        equityAccount.setName(\"Equity Account\");\n        e.addAccount(e.getRootAccount(), equityAccount);\n\n        Account liabilityAccount = new Account(AccountType.LIABILITY, defaultCurrency);\n        liabilityAccount.setName(\"Liability Account\");\n        e.addAccount(e.getRootAccount(), liabilityAccount);\n\n        investAccount = new Account(AccountType.INVEST, defaultCurrency);\n        investAccount.setName(\"Invest Account\");\n        e.addAccount(e.getRootAccount(), investAccount);\n\n        // Creating securities\n        gggSecurityNode = new SecurityNode(defaultCurrency);\n        gggSecurityNode.setSymbol(\"GOOGLE\");\n        assertTrue(e.addSecurity(gggSecurityNode));\n\n        // Adding security to the invest account\n        List<SecurityNode> securityNodeList = new ArrayList<>();\n        securityNodeList.add(gggSecurityNode);\n        assertTrue(e.updateAccountSecurities(investAccount, securityNodeList));\n    }\n\n    @AfterEach\n    public void tearDown() throws IOException {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n        EngineFactory.deleteDatabase(database);\n\n        Files.deleteIfExists(Paths.get(database));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/AccountTreeDepthTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received account copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage jgnash.engine;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\n/**\n * Tests account ancestor depth API.\n *\n * @author Craig Cavanaugh\n */\nclass AccountTreeDepthTest {\n\n    AccountTreeDepthTest() {\n    }\n\n    @Test\n    void getDepthTest() {\n        CurrencyNode defaultCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n        RootAccount root = new RootAccount(defaultCurrency);\n\n        Account a = new Account(AccountType.BANK, defaultCurrency);\n        a.setName(\"a\");\n        root.addChild(a);\n\n        Account b = new Account(AccountType.BANK, defaultCurrency);\n        b.setName(\"b\");\n        a.addChild(b);\n\n        Account c = new Account(AccountType.BANK, defaultCurrency);\n        c.setName(\"c\");\n        b.addChild(c);\n\n        Account d = new Account(AccountType.BANK, defaultCurrency);\n        d.setName(\"d\");\n        c.addChild(d);\n\n        System.out.println(root.getDepth());\n        assertEquals(0, root.getDepth());\n\n        System.out.println(a.getDepth());\n        assertEquals(1, a.getDepth());\n\n        System.out.println(b.getDepth());\n        assertEquals(2, b.getDepth());\n\n        System.out.println(c.getDepth());\n        assertEquals(3, c.getDepth());\n\n        System.out.println(d.getDepth());\n        assertEquals(4, d.getDepth());\n\n        assertNotNull(AccountUtils.searchTree(root, \"d\", AccountType.BANK, 4));\n        assertNull(AccountUtils.searchTree(root, \"d\", AccountType.BANK, 3));\n    }\n\n    @Test\n    void getLineageTest() {\n        CurrencyNode defaultCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n        RootAccount root = new RootAccount(defaultCurrency);\n\n        Account a = new Account(AccountType.BANK, defaultCurrency);\n        a.setName(\"a\");\n        root.addChild(a);\n\n        Account a1 = new Account(AccountType.BANK, defaultCurrency);\n        a1.setName(\"a1\");\n        root.addChild(a1);\n\n        Account b = new Account(AccountType.BANK, defaultCurrency);\n        b.setName(\"b\");\n        a.addChild(b);\n\n        Account c = new Account(AccountType.BANK, defaultCurrency);\n        c.setName(\"c\");\n        b.addChild(c);\n\n        Account d = new Account(AccountType.BANK, defaultCurrency);\n        d.setName(\"d\");\n        c.addChild(d);\n\n        Account e = new Account(AccountType.BANK, defaultCurrency);\n        e.setName(\"e\");\n        c.addChild(e);        \n    }\n\n    @Test\n    void getAncestorsTest() {\n        CurrencyNode defaultCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n        RootAccount root = new RootAccount(defaultCurrency);\n\n        Account a = new Account(AccountType.BANK, defaultCurrency);\n        a.setName(\"a\");\n        root.addChild(a);\n\n        Account a1 = new Account(AccountType.BANK, defaultCurrency);\n        a1.setName(\"a1\");\n        root.addChild(a1);\n\n        Account b = new Account(AccountType.BANK, defaultCurrency);\n        b.setName(\"b\");\n        a.addChild(b);\n\n        Account c = new Account(AccountType.BANK, defaultCurrency);\n        c.setName(\"c\");\n        b.addChild(c);\n\n        Account d = new Account(AccountType.BANK, defaultCurrency);\n        d.setName(\"d\");\n        c.addChild(d);\n\n        Account e = new Account(AccountType.BANK, defaultCurrency);\n        e.setName(\"e\");\n        c.addChild(e);\n\n        List<Account> ancestors = e.getAncestors();\n        assertEquals(5, ancestors.size());\n        assertEquals(AccountType.ROOT, ancestors.get(ancestors.size() - 1).getAccountType());\n    }\n\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/BinaryXStreamEngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.AfterAll;\n\n/**\n * Engine text for Binary XStream.\n *\n * @author Craig Cavanaugh\n */\npublic class BinaryXStreamEngineTest extends EngineTest {\n\n    private static String tempFile;\n\n    @Override\n    public Engine createEngine() {\n        try {\n            testFile = Files.createTempFile(\"jgnash-\", DataStoreType.BINARY_XSTREAM.getDataStore().getFileExt())\n                    .toString();\n\n            tempFile = testFile;\n\n        } catch (final IOException e1) {\n            Logger.getLogger(BinaryXStreamEngineTest.class.getName()).log(Level.SEVERE, e1.getLocalizedMessage(), e1);\n        }\n\n        EngineFactory.deleteDatabase(testFile);\n\n        return EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.BINARY_XSTREAM);\n    }\n\n    @AfterAll\n    static void cleanup() throws IOException {\n        Files.deleteIfExists(Paths.get(tempFile));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/CashFlowTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.Month;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * \n * @author t-pa\n */\nclass CashFlowTest {\n    \n    @Test\n    void testPositiveIRR() {\n        CashFlow cashFlow = new CashFlow();\n        \n        LocalDate today = LocalDate.now();\n        cashFlow.add(today, BigDecimal.valueOf(-100));\n        cashFlow.add(today.plusDays(365), BigDecimal.valueOf(103));\n        \n        double irr = cashFlow.internalRateOfReturn();\n        assertEquals(0.03, irr,1.e-5);\n    }\n\n    @Test\n    void testNegativeIRR() {\n        CashFlow cashFlow = new CashFlow();\n        \n        LocalDate today = LocalDate.now();\n        cashFlow.add(today, BigDecimal.valueOf(97));\n        cashFlow.add(today.minusDays(365), BigDecimal.valueOf(-100));\n        \n        double irr = cashFlow.internalRateOfReturn();\n        assertEquals(-0.03, irr,1.e-5);\n    }\n\n    @Test\n    void testUglyData() {\n        CashFlow cashFlow = new CashFlow();\n\n        cashFlow.add(LocalDate.of(2012, Month.JANUARY, 9), new BigDecimal(\"0.1\"));\n        cashFlow.add(LocalDate.of(2012, Month.FEBRUARY, 2), new BigDecimal(\"1.03\"));\n        cashFlow.add(LocalDate.of(2012, Month.MARCH, 12), new BigDecimal(\"1.04\"));\n        cashFlow.add(LocalDate.of(2012, Month.APRIL, 12), new BigDecimal(\"0.91\"));\n        cashFlow.add(LocalDate.of(2012, Month.MAY, 10), new BigDecimal(\"0.87\"));\n        cashFlow.add(LocalDate.of(2012, Month.JUNE, 12), new BigDecimal(\"0.9\"));\n        cashFlow.add(LocalDate.of(2012, Month.JULY, 12), new BigDecimal(\"0.84\"));\n        cashFlow.add(LocalDate.of(2012, Month.AUGUST, 12), new BigDecimal(\"0.83\"));\n        cashFlow.add(LocalDate.of(2012, Month.SEPTEMBER, 13), new BigDecimal(\"0.83\"));\n        cashFlow.add(LocalDate.of(2012, Month.OCTOBER, 10), new BigDecimal(\"0.79\"));\n        cashFlow.add(LocalDate.of(2012, Month.NOVEMBER, 12), new BigDecimal(\"0.92\"));\n        cashFlow.add(LocalDate.of(2012, Month.DECEMBER, 2), new BigDecimal(\"8.885624\"));\n\n        double irr = cashFlow.internalRateOfReturn();\n\n        assertEquals(Double.NaN, irr);\n    }\n\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/ConfigTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * Unit for the Config API.\n *\n * @author Craig Cavanaugh\n */\nclass ConfigTest {\n\n    @Test\n    void testFileFormat() {\n        Config config = new Config();\n\n        assertEquals(Engine.CURRENT_MAJOR_VERSION, config.getMajorFileFormatVersion());\n\n        assertEquals(Engine.CURRENT_MINOR_VERSION, config.getMinorFileFormatVersion());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/DataStoreTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n/**\n * Unit test for DataStoreTypes.\n *\n * @author Craig Cavanaugh\n */\nclass DataStoreTest {\n\n    DataStoreTest() {\n    }\n\n    @Test\n    void instanceTest() {\n        assertNotNull(DataStoreType.H2_DATABASE.getDataStore().toString());\n        System.out.println(DataStoreType.H2_DATABASE.getDataStore());\n\n        assertNotNull(DataStoreType.XML.getDataStore().toString());\n        System.out.println(DataStoreType.XML.getDataStore());\n    }\n}"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/DistributedLockTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.engine.concurrent.DistributedLockManager;\nimport jgnash.engine.concurrent.DistributedLockServer;\n\nimport io.netty.util.ResourceLeakDetector;\nimport jgnash.util.LogUtil;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * Test to validate the Distributed lock manager.\n *\n * @author Craig Cavanaugh\n */\npublic class DistributedLockTest {\n\n    static final int PORT = 5002;\n\n    DistributedLockServer server;\n\n    DistributedLockManager manager;\n\n    private static final Logger logger = Logger.getLogger(DistributedLockTest.class.getName());\n\n    private final Random random = new Random();\n\n    @BeforeAll\n    static void beforeAll() {\n        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);\n\n        LogUtil.configureLogging();\n    }\n\n    @BeforeEach\n    public void setUp() {\n\n        server = new DistributedLockServer(PORT);\n        assertTrue(server.startServer(EngineFactory.EMPTY_PASSWORD));\n\n        manager = new DistributedLockManager(EngineFactory.LOCALHOST, PORT);\n        manager.connectToServer(EngineFactory.EMPTY_PASSWORD);\n    }\n\n    @AfterEach\n    void tearDown() {\n        manager.disconnectFromServer();\n        server.stopServer();\n    }\n\n    @Test\n    void simpleLock() {\n        final ReadWriteLock accountLock = manager.getLock(\"account\");\n        final ReadWriteLock transactionLock = manager.getLock(\"transaction\");\n\n        try {\n            accountLock.readLock().lock();\n            transactionLock.writeLock().lock();\n            Thread.sleep(1000);\n        } catch (final InterruptedException e) {\n            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n            fail();\n        } finally {\n            accountLock.readLock().unlock();\n            transactionLock.writeLock().unlock();\n        }\n\n        assertTrue(true);\n    }\n\n    @Test\n    void multipleReadLocks() {\n\n        final int TIME_BOUND = 3000;\n\n        final int TEST_COUNT = 33;\n\n        final AtomicInteger threadCount = new AtomicInteger();\n\n        class WriteLockTest extends Thread {\n\n            private WriteLockTest() {\n                setName(\"DistributedLockTest WriteLockTest Thread \" + threadCount.incrementAndGet());\n            }\n\n            @Override\n            public void run() {\n                final ReentrantReadWriteLock lock = manager.getLock(\"lockTest\");\n                logger.info(\"fetched lock \" + getId());\n\n\n                logger.log(Level.INFO, \"locking: {0}\", getId());\n                lock.readLock().lock();\n\n                try {\n\n                    int num = random.nextInt(TIME_BOUND);\n\n                    try {\n                        Thread.sleep(num);\n                    } catch (final InterruptedException e) {\n                        logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                        fail();\n                    }\n\n                } finally {\n                    logger.log(Level.INFO, \"unlocking: {0}\", getId());\n                    lock.readLock().unlock();\n                }\n            }\n        }\n\n        List<WriteLockTest> lockTests = new ArrayList<>();\n\n        for (int i = 0; i < TEST_COUNT; i++) {\n            WriteLockTest thread = new WriteLockTest();\n            lockTests.add(thread);\n        }\n\n        for (final WriteLockTest test : lockTests) {\n            test.start();\n        }\n\n        try {\n            for (final WriteLockTest test : lockTests) {\n                test.join(TIME_BOUND);\n            }\n        } catch (final InterruptedException e) {\n            Logger.getLogger(DistributedLockTest.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            fail();\n        }\n\n        assertTrue(true);\n    }\n\n    @Test\n    void writeLockTest() throws InterruptedException {\n\n        class ReadLockThread implements Runnable {\n            final private ReadWriteLock readWriteLock;\n\n            private ReadLockThread(final ReadWriteLock readWriteLock) {\n                this.readWriteLock = readWriteLock;\n            }\n\n            @Override\n            public void run() {\n                try {\n                    logger.info(\"try read lock\");\n                    readWriteLock.readLock().lock();\n                    logger.info(\"got read lock\");\n                } finally {\n                    logger.info(\"unlock read lock\");\n                    readWriteLock.readLock().unlock();\n                }\n            }\n        }\n\n        class WriteLockThread implements Runnable {\n            final private ReadWriteLock readWriteLock;\n\n            private WriteLockThread(final ReadWriteLock readWriteLock) {\n                this.readWriteLock = readWriteLock;\n            }\n\n            @Override\n            public void run() {\n                try {\n                    logger.info(\"try write lock\");\n                    readWriteLock.writeLock().lock();\n                    logger.info(\"got write lock\");\n\n                    Thread.sleep(1500);\n                } catch (InterruptedException e) {\n                    logger.log(Level.SEVERE, e.getLocalizedMessage(), e);\n                } finally {\n                    logger.info(\"unlock write lock\");\n                    readWriteLock.writeLock().unlock();\n                }\n            }\n        }\n\n        ReadWriteLock rwLock = manager.getLock(\"test-lock\");\n\n        Thread writeLockThread = new Thread(new WriteLockThread(rwLock));\n        Thread readLockThread = new Thread(new ReadLockThread(rwLock));\n\n        writeLockThread.start();\n        Thread.sleep(100);\n        readLockThread.start();\n\n        writeLockThread.join();\n        readLockThread.join();\n    }\n\n    @Test\n    void reentrantWriteTest() {\n        int count = 0;\n\n        ReadWriteLock lock1 = manager.getLock(\"reentrant\");\n        ReadWriteLock lock2 = manager.getLock(\"reentrant2\");\n\n        try {\n            lock1.writeLock().lock();\n            lock2.writeLock().lock();\n            count++;\n\n            lock1.writeLock().lock();\n            lock2.writeLock().lock();\n            count++;\n\n            lock1.writeLock().lock();\n            lock2.writeLock().lock();\n            count++;\n\n            lock1.writeLock().lock();\n            lock2.writeLock().lock();\n            count++;\n\n        } finally {\n            lock1.writeLock().unlock();\n            lock1.writeLock().unlock();\n            lock1.writeLock().unlock();\n            lock1.writeLock().unlock();\n\n            lock2.writeLock().unlock();\n            lock2.writeLock().unlock();\n            lock2.writeLock().unlock();\n            lock2.writeLock().unlock();\n        }\n\n        assertEquals(4, count);\n    }\n\n    @Test\n    void reentrantReadTest() {\n        int count = 0;\n\n        ReadWriteLock lock = manager.getLock(\"reentrant\");\n\n        try {\n            lock.readLock().lock();\n            count++;\n\n            lock.readLock().lock();\n            count++;\n\n            lock.readLock().lock();\n            count++;\n\n            lock.readLock().lock();\n            count++;\n\n        } finally {\n            lock.readLock().unlock();\n            lock.readLock().unlock();\n            lock.readLock().unlock();\n            lock.readLock().unlock();\n        }\n\n        assertEquals(4, count);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/EncryptedDistributedLockTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.engine.concurrent.DistributedLockManager;\nimport jgnash.engine.concurrent.DistributedLockServer;\nimport org.junit.jupiter.api.BeforeEach;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * Test to validate the Distributed lock manager.\n *\n * @author Craig Cavanaugh\n */\npublic class EncryptedDistributedLockTest extends DistributedLockTest {\n\n    @BeforeEach\n    @Override\n    public void setUp() {\n        final char[] password = new char[]{'P', 'a', 's', 's', 'w', 'o', 'r', 'd'};\n\n        //System.setProperty(EncryptionManager.ENCRYPTION_FLAG, \"true\");\n        //System.setProperty(\"ssl\", \"true\");\n\n        server = new DistributedLockServer(PORT);\n        assertTrue(server.startServer(password));\n\n        manager = new DistributedLockManager(EngineFactory.LOCALHOST, PORT);\n        manager.connectToServer(password);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/EngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.budget.BudgetGoal;\nimport jgnash.engine.recurring.DailyReminder;\nimport jgnash.engine.recurring.Reminder;\nimport jgnash.time.Period;\nimport jgnash.util.FileUtils;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * Abstract base class for testing the Engine API.\n *\n * @author Craig Cavanaugh\n */\npublic abstract class EngineTest {\n\n    private static final float DELTA = .001f;\n\n    private Engine e;\n\n    String testFile;\n\n    protected abstract Engine createEngine() throws Exception;\n\n    private static void closeEngine() {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n    }\n\n    @BeforeEach\n    void setUp() throws Exception {\n        Locale.setDefault(Locale.US);\n\n        e = createEngine();\n\n        assertNotNull(e);   // fail if null\n\n        e.setCreateBackups(false);\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        if (!node.getSymbol().equals(\"USD\")) {\n            CurrencyNode defaultCurrency = DefaultCurrencies.buildNode(Locale.US);\n\n            assertNotNull(defaultCurrency);\n            assertTrue(e.addCurrency(defaultCurrency));\n\n            e.setDefaultCurrency(defaultCurrency);\n        }\n\n        node = e.getCurrency(\"CAD\");\n\n        if (node == null) {\n            node = DefaultCurrencies.buildNode(Locale.CANADA);\n\n            assertNotNull(node);\n\n            assertTrue(e.addCurrency(node));\n        }\n\n        // close the file/engine\n        closeEngine();\n\n        // check for correct file version\n        final float version = EngineFactory.getFileVersion(Paths.get(testFile), EngineFactory.EMPTY_PASSWORD);\n        final Config config = new Config();\n        assertEquals(Float.parseFloat(config.getFileFormat()), version, .0001);\n\n        // reopen the file for more tests\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertNotNull(e);\n    }\n\n    @AfterEach\n    void tearDown() throws IOException {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        Files.deleteIfExists(Paths.get(testFile));\n\n        final String attachmentDir = System.getProperty(\"java.io.tmpdir\") + System.getProperty(\"path.SEPARATOR\")\n                                             + \"attachments\";\n        final Path directory = Paths.get(attachmentDir);\n\n        FileUtils.deletePathAndContents(directory);\n    }\n\n    @Test\n    void testGetName() {\n        assertEquals(e.getName(), EngineFactory.DEFAULT);\n    }\n\n    @Test\n    void testReminders() throws CloneNotSupportedException {\n\n        assertEquals(0, e.getReminders().size());\n        assertEquals(0, e.getPendingReminders().size());\n\n        Reminder r = new DailyReminder();\n\n        r.setIncrement(1);\n        r.setEndDate(null);\n        assertFalse(e.addReminder(r));  // should fail because description is not set\n\n        r.setDescription(\"test\");\n        assertTrue(e.addReminder(r));   // should pass now\n\n        assertEquals(1, e.getReminders().size());\n        assertEquals(1, e.getPendingReminders().size());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        assertEquals(e.getReminders().size(), 1);\n\n        // Clone reminders\n        r = e.getReminders().get(0);\n        assertNotNull(r);\n        Reminder clone = (Reminder) r.clone();\n        assertNotNull(clone);\n\n        assertNotEquals(clone, r);\n        assertNotEquals(0, clone.compareTo(r));\n\n        // remove a reminder\n        e.removeReminder(e.getReminders().get(0));\n        assertEquals(0, e.getReminders().size());\n        assertEquals(0, e.getPendingReminders().size());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        assertEquals(0, e.getReminders().size());\n        assertEquals(0, e.getPendingReminders().size());\n    }\n\n    @Test\n    void testTags() {\n        final Account a = new Account(AccountType.BANK, e.getDefaultCurrency());\n        a.setName(\"Tag Test Account\");\n\n        e.addAccount(e.getRootAccount(), a);\n\n        final Tag tag1 = new Tag();\n        tag1.setName(\"tag1\");\n        assertTrue(e.addTag(tag1));\n\n        final Tag tag2 = new Tag();\n        tag2.setName(\"tag2\");\n        assertTrue(e.addTag(tag2));\n\n        final Tag tag3 = new Tag();\n        tag3.setName(\"tag3\");\n        assertTrue(e.addTag(tag3));\n\n        final Transaction singleEntryTransaction = TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(),\n                \"tagTest\", \"tag test payee\", \"1001\");\n\n        TransactionEntry entry = singleEntryTransaction.getTransactionEntries().get(0);\n\n        entry.setTags(e.getTags());\n\n        final UUID uuid = singleEntryTransaction.getUuid(); // get uuid for easy lookup\n\n        assertTrue(e.addTransaction(singleEntryTransaction));\n\n        Transaction transaction = e.getTransactionByUuid(uuid);\n        assertNotNull(transaction);\n\n        Set<Tag> reTags = transaction.getTransactionEntries().get(0).getTags();\n        assertEquals(3, reTags.size());\n\n        for (final Tag tag : e.getTags()) {\n            if (tag.getName().equals(\"tag1\")) {\n                tag.setDescription(\"description 1\");\n                assertTrue(e.updateTag(tag));\n            }\n        }\n\n        for (final Tag tag : e.getTags()) {\n            if (tag.getName().equals(\"tag1\")) {\n                assertEquals(\"description 1\", tag.getDescription());\n            }\n        }\n\n        final Tag tag4 = new Tag();\n        tag4.setName(\"tag4\");\n        assertTrue(e.addTag(tag4));\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        assertNotNull(e);\n\n        assertEquals(4, e.getTags().size());\n        assertEquals(3, e.getTagsInUse().size());\n\n        transaction = e.getTransactionByUuid(uuid);\n        assertNotNull(transaction);\n\n        reTags = transaction.getTransactionEntries().get(0).getTags();\n        assertEquals(3, reTags.size());\n\n        for (final Tag tag : e.getTags()) {\n            if (tag.getName().equals(\"tag1\")) {\n                assertEquals(\"description 1\", tag.getDescription());\n            }\n        }\n\n        assertEquals(3, transaction.getTags().size());\n\n        for (final Tag tag : e.getTags()) {\n            if (tag.getName().equals(\"tag4\")) {\n                assertTrue(e.removeTag(tag));\n            } else {\n                assertFalse(e.removeTag(tag));\n            }\n        }\n    }\n\n    @Test\n    void testGetStoredObjectByUuid() {\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertNotNull(e);\n\n        UUID uuid = e.getDefaultCurrency().getUuid();\n\n        assertSame(e.getDefaultCurrency(), e.getCurrencyNodeByUuid(uuid));\n    }\n\n    @Test\n    void testGetStoredObjects() {\n        assertFalse(e.getStoredObjects().isEmpty());\n    }\n\n    @Test\n    void testAddCommodity() {\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertNotNull(e.getDefaultCurrency());\n        assertNotNull(e.getCurrency(\"USD\"));\n        assertNotNull(e.getCurrency(\"CAD\"));\n    }\n\n    @Test\n    void testPreferences() {\n        e.setPreference(\"myKey\", \"myValue\");\n        e.setPreference(\"myNumber\", BigDecimal.TEN.toString());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertEquals(\"myValue\", e.getPreference(\"myKey\"));\n        assertEquals(BigDecimal.TEN, new BigDecimal(e.getPreference(\"myNumber\")));\n    }\n\n    @Test\n    void testSecurityNodeStorage() {\n\n        final String SECURITY_SYMBOL = \"GOOG\";\n\n        SecurityNode securityNode = new SecurityNode(e.getDefaultCurrency());\n\n        securityNode.setSymbol(SECURITY_SYMBOL);\n        securityNode.setScale((byte) 2);\n\n        assertTrue(e.addSecurity(securityNode));\n\n        SecurityHistoryNode historyNode = new SecurityHistoryNode(LocalDate.now(), BigDecimal.valueOf(125), 10000,\n                BigDecimal.valueOf(126), BigDecimal.valueOf(124));\n\n        e.addSecurityHistory(securityNode, historyNode);\n\n\n        final SecurityHistoryEvent dividendEvent = new SecurityHistoryEvent(SecurityHistoryEventType.DIVIDEND,\n                LocalDate.now(), BigDecimal.ONE);\n        assertTrue(e.addSecurityHistoryEvent(securityNode, dividendEvent));\n\n\n        final SecurityHistoryEvent splitEvent = new SecurityHistoryEvent(SecurityHistoryEventType.SPLIT,\n                LocalDate.now(), BigDecimal.TEN);\n        assertTrue(e.addSecurityHistoryEvent(securityNode, splitEvent));\n\n        assertTrue(securityNode.getHistoryEvents().contains(dividendEvent));\n        assertTrue(securityNode.getHistoryEvents().contains(splitEvent));\n\n        assertEquals(2, securityNode.getHistoryEvents().size());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        securityNode = e.getSecurity(SECURITY_SYMBOL);\n\n        assertNotNull(securityNode);\n\n        assertEquals(2, securityNode.getHistoryEvents().size());\n\n        List<SecurityHistoryEvent> events = new ArrayList<>(securityNode.getHistoryEvents());\n\n        assertTrue(e.removeSecurityHistoryEvent(securityNode, events.get(0)));\n        assertTrue(e.removeSecurityHistoryEvent(securityNode, events.get(1)));\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        securityNode = e.getSecurity(SECURITY_SYMBOL);\n\n        assertNotNull(securityNode);\n\n        assertEquals(0, securityNode.getHistoryEvents().size());\n\n        Account a = new Account(AccountType.INVEST, e.getDefaultCurrency());\n        a.setName(\"Invest\");\n        assertTrue(e.addAccount(e.getRootAccount(), a));\n\n        // try again\n        assertFalse(e.addAccount(e.getRootAccount(), a));\n\n        assertTrue(e.updateAccountSecurities(a, Collections.singletonList(securityNode)));\n        assertEquals(1, a.getSecurities().size());\n\n        assertTrue(e.updateAccountSecurities(a, Collections.emptyList()));\n        assertEquals(0, a.getSecurities().size());\n    }\n\n    @Test\n    void exchangeRateTest1() {\n\n        CurrencyNode usdNode = new CurrencyNode();\n        usdNode.setSymbol(\"USD\");\n        usdNode.setPrefix(\"$\");\n        usdNode.setDescription(\"US Dollar\");\n        e.addCurrency(usdNode);\n\n        CurrencyNode cadNode = new CurrencyNode();\n        cadNode.setSymbol(\"CAD\");\n        cadNode.setPrefix(\"$\");\n        cadNode.setDescription(\"CAD Dollar\");\n        e.addCurrency(cadNode);\n\n        assertNotNull(usdNode.getSymbol());\n        assertNotNull(cadNode.getSymbol());\n\n        e.setExchangeRate(usdNode, cadNode, new BigDecimal(\"1.100\"));\n\n        assertEquals(new BigDecimal(\"1.100\"), usdNode.getExchangeRate(cadNode));\n        assertEquals(new BigDecimal(\"0.909\"), cadNode.getExchangeRate(usdNode).setScale(3, RoundingMode.DOWN));\n\n        assertEquals(BigDecimal.ONE, usdNode.getExchangeRate(usdNode));\n        assertEquals(BigDecimal.ONE, cadNode.getExchangeRate(cadNode));\n\n        assertTrue(e.removeCommodity(cadNode));\n    }\n\n    @Test\n    void exchangeRateTest2() {\n\n        CurrencyNode usdNode = new CurrencyNode();\n        usdNode.setSymbol(\"USD\");\n        usdNode.setPrefix(\"$\");\n        usdNode.setDescription(\"US Dollar\");\n        e.addCurrency(usdNode);\n\n        CurrencyNode cadNode = new CurrencyNode();\n        cadNode.setSymbol(\"CAD\");\n        cadNode.setPrefix(\"$\");\n        cadNode.setDescription(\"CAD Dollar\");\n        e.addCurrency(cadNode);\n\n        assertNotNull(usdNode.getSymbol());\n        assertNotNull(cadNode.getSymbol());\n\n        // rate is inverted when added\n        e.setExchangeRate(cadNode, usdNode, new BigDecimal(\"0.909\"));\n\n        assertEquals(new BigDecimal(\"1.100\"), usdNode.getExchangeRate(cadNode).setScale(3, RoundingMode.DOWN));\n        assertEquals(new BigDecimal(\"0.909\"), cadNode.getExchangeRate(usdNode).setScale(3, RoundingMode.DOWN));\n\n\n        assertTrue(e.removeCommodity(cadNode));\n\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n    }\n\n    @Test\n    void commodityNodeStoreTest() {\n        CurrencyNode node = new CurrencyNode();\n\n        node.setSymbol(\"USD\");\n        node.setPrefix(\"$\");\n        node.setDescription(\"US Dollar\");\n\n        e.addCurrency(node);\n\n        node = e.getCurrency(\"USD\");\n\n        Account account = new Account(AccountType.BANK, node);\n        account.setName(\"Bank Account\");\n\n        e.addAccount(e.getRootAccount(), account);\n\n        Object cNode = e.getCurrency(\"USD\");\n\n        //noinspection ConstantConditions\n        assertTrue(cNode instanceof CurrencyNode, \"Returned object extends CurrencyNode\");\n\n        //noinspection ConstantConditions\n        assertTrue(cNode instanceof StoredObject, \"Returned object extends StoredObject\");\n\n        Set<CurrencyNode> nodes = DefaultCurrencies.generateCurrencies();\n\n        for (final CurrencyNode n : nodes) {\n            e.addCurrency(n);\n        }\n\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n    }\n\n    @Test\n    void testCurrencyRemove() {\n        CurrencyNode currencyNode = new CurrencyNode();\n        currencyNode.setDescription(\"Test\");\n        currencyNode.setSymbol(\"BTC\");\n        currencyNode.setPrefix(\"$\");\n        currencyNode.setScale((byte) 8);\n\n        int oldCount = e.getCurrencies().size();\n        e.addCurrency(currencyNode);\n\n        assertEquals(oldCount + 1, e.getCurrencies().size());\n\n        currencyNode = e.getCurrency(\"BTC\");\n\n        assertNotNull(currencyNode);\n\n        e.removeCommodity(currencyNode);\n        assertEquals(oldCount, e.getCurrencies().size());\n    }\n\n    @Test\n    void testBudget() {\n\n        final String ACCOUNT_NAME = \"testAccount\";\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        e.addAccount(e.getRootAccount(), a);\n\n        assertEquals(0, e.getBudgetList().size());\n\n        Budget budget = new Budget();\n        budget.setName(\"Default\");\n        budget.setDescription(\"Default Budget\");\n\n        BigDecimal[] budgetGoals = new BigDecimal[BudgetGoal.PERIODS];\n\n        BudgetGoal goal = new BudgetGoal();\n\n        // load the goals\n        for (int i = 0; i < budgetGoals.length; i++) {\n            budgetGoals[i] = new BigDecimal(i);\n        }\n        goal.setGoals(budgetGoals);\n        goal.setBudgetPeriod(Period.WEEKLY);\n\n        budget.setBudgetGoal(a, goal);\n        budget.setBudgetPeriod(Period.WEEKLY);\n\n        assertTrue(e.addBudget(budget));\n\n        assertEquals(1, e.getBudgetList().size());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertNotNull(e);\n\n        assertEquals(1, e.getBudgetList().size());\n\n        a = e.getAccountByName(ACCOUNT_NAME);\n        assertNotNull(a);\n\n        Budget recovered = e.getBudgetList().get(0);\n        budgetGoals = recovered.getBudgetGoal(a).getGoals();\n\n        // check the goals\n        assertEquals(BudgetGoal.PERIODS, budgetGoals.length);\n\n        // check the periods\n        assertEquals(Period.WEEKLY, recovered.getBudgetPeriod());\n        assertEquals(Period.WEEKLY, recovered.getBudgetGoal(a).getBudgetPeriod());\n\n        for (int i = 0; i < budgetGoals.length; i++) {\n            //assertEquals(new BigDecimal(i), budgetGoals[i]);\n            //assertThat(new BigDecimal(i), is(closeTo(budgetGoals[i], new BigDecimal(0.0001))));\n            assertEquals(i, budgetGoals[i].doubleValue(), 0.0001);\n        }\n\n        // remove a budget\n        assertTrue(e.removeBudget(e.getBudgetList().get(0)));\n        assertEquals(0, e.getBudgetList().size());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        assertEquals(0, e.getBudgetList().size());\n    }\n\n    @Test\n    void testGetActiveCurrencies() {\n        Set<CurrencyNode> nodes = e.getActiveCurrencies();\n\n        assertNotNull(nodes);\n\n        assertFalse(nodes.isEmpty());\n\n        System.out.println(\"Node count is \" + nodes.size());\n    }\n\n    @Test\n    void testGetCurrencyStringLocale() {\n        CurrencyNode currency = e.getCurrency(\"USD\");\n        assertNotNull(currency);\n    }\n\n    @Test\n    void testGetCurrencyString() {\n        CurrencyNode currency = e.getCurrency(\"USD\");\n        assertNotNull(currency);\n\n        currency = e.getCurrency(\"CAD\");\n        assertNotNull(currency);\n    }\n\n    @Test\n    void testGetCurrencies() {\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        List<CurrencyNode> nodes = e.getCurrencies();\n        assertNotNull(nodes);\n        System.out.println(nodes.size());\n        assertTrue(nodes.size() > 1);\n    }\n\n\n    @Test\n    void testSecurityHistory() {\n        BigDecimal securityPrice1 = new BigDecimal(\"2.00\");\n\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n\n        SecurityNode securityNode1 = new SecurityNode(e.getDefaultCurrency());\n        securityNode1.setSymbol(\"GOOGLE\");\n        assertTrue(e.addSecurity(securityNode1));\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(securityNode1, history));\n\n        assertEquals(1, securityNode1.getHistoryNodes().size());\n\n        // Same date and price, new instance\n        history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(securityNode1, history));   // should replace\n        assertEquals(1, securityNode1.getHistoryNodes().size());\n\n        // Same date, new instance and updated price\n        history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(new BigDecimal(\"2.01\"));\n\n        assertTrue(e.addSecurityHistory(securityNode1, history));  // should replace\n        assertEquals(1, securityNode1.getHistoryNodes().size());\n\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n\n        history = new SecurityHistoryNode();\n        history.setDate(transactionDate2);\n        history.setPrice(new BigDecimal(\"2.02\"));\n        assertTrue(e.addSecurityHistory(securityNode1, history));  // should be okay\n        assertEquals(2, securityNode1.getHistoryNodes().size());\n\n        final SecurityHistoryEvent dividendEvent = new SecurityHistoryEvent(SecurityHistoryEventType.DIVIDEND, LocalDate.now(), BigDecimal.ONE);\n        securityNode1.addSecurityHistoryEvent(dividendEvent);\n\n\n        final SecurityHistoryEvent splitEvent = new SecurityHistoryEvent(SecurityHistoryEventType.SPLIT, LocalDate.now(), BigDecimal.TEN);\n        securityNode1.addSecurityHistoryEvent(splitEvent);\n\n        assertTrue(securityNode1.getHistoryEvents().contains(dividendEvent));\n        assertTrue(securityNode1.getHistoryEvents().contains(splitEvent));\n\n        assertEquals(2, securityNode1.getHistoryEvents().size());\n\n        securityNode1.removeSecurityHistoryEvent(dividendEvent);\n        assertEquals(1, securityNode1.getHistoryEvents().size());\n\n        securityNode1.removeSecurityHistoryEvent(splitEvent);\n        assertEquals(0, securityNode1.getHistoryEvents().size());\n    }\n\n    @Test\n    void testGetExchangeRate() {\n\n        final LocalDate today = LocalDate.now();\n        final LocalDate yesterday = today.minusDays(1);\n\n        CurrencyNode usd = e.getCurrency(\"USD\");\n        CurrencyNode cad = e.getCurrency(\"CAD\");\n\n        e.setExchangeRate(usd, cad, new BigDecimal(\"1.02\"), LocalDate.now());\n        e.setExchangeRate(usd, cad, new BigDecimal(\"1.01\"), LocalDate.now().minusDays(1));\n\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        ExchangeRate rate = e.getExchangeRate(usd, cad);\n        assertNotNull(rate);\n\n        assertEquals(0, new BigDecimal(\"1.02\").compareTo(rate.getRate()));\n        assertEquals(0, new BigDecimal(\"1.01\").compareTo(rate.getRate(yesterday)));\n    }\n\n\n    @Test\n    void testGetDefaultCurrency() {\n        CurrencyNode defaultCurrency = e.getDefaultCurrency();\n\n        assertNotNull(defaultCurrency);\n        assertEquals(defaultCurrency.getSymbol(), \"USD\");\n    }\n\n    @Test\n    void testRemoveExchangeRateHistory() {\n\n        final LocalDate testDate = LocalDate.now().minusYears(2);\n\n        CurrencyNode usdCurrency = e.getDefaultCurrency();\n        assertEquals(usdCurrency.getSymbol(), \"USD\");\n\n        CurrencyNode cadCurrency = e.getCurrency(\"CAD\");\n        assertEquals(cadCurrency.getSymbol(), \"CAD\");\n\n        e.setExchangeRate(usdCurrency, cadCurrency, new BigDecimal(\"0.5\"), testDate);\n\n        ExchangeRate exchangeRate = e.getExchangeRate(usdCurrency, cadCurrency);\n        assertNotNull(exchangeRate);\n\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        usdCurrency = e.getDefaultCurrency();\n        cadCurrency = e.getCurrency(\"CAD\");\n        exchangeRate = e.getExchangeRate(usdCurrency, cadCurrency);\n        assertNotNull(exchangeRate);\n\n        ExchangeRateHistoryNode historyNode = exchangeRate.getHistory(testDate);\n        assertNotNull(historyNode);\n\n        e.removeExchangeRateHistory(exchangeRate, historyNode);\n        historyNode = exchangeRate.getHistory(testDate);\n        assertNull(historyNode);\n\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        usdCurrency = e.getDefaultCurrency();\n        cadCurrency = e.getCurrency(\"CAD\");\n        exchangeRate = e.getExchangeRate(usdCurrency, cadCurrency);\n        assertNotNull(exchangeRate);\n        historyNode = exchangeRate.getHistory(testDate);\n        assertNull(historyNode);\n    }\n\n    @Test\n    void testUpdateCommodity() throws CloneNotSupportedException {\n        CurrencyNode testNode = DefaultCurrencies.buildCustomNode(\"CAD\");\n        assertNotNull(testNode);\n        e.addCurrency(testNode);\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        testNode = e.getCurrency(\"CAD\");\n        assertNotNull(testNode);\n\n        final CurrencyNode clone = (CurrencyNode) testNode.clone();\n        clone.setDescription(\"changed\");\n        e.updateCommodity(testNode, clone);\n\n        testNode = e.getCurrency(\"CAD\");\n        assertNotNull(testNode);\n\n        assertEquals(\"changed\", testNode.getDescription());\n\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n        testNode = e.getCurrency(\"CAD\");\n        assertNotNull(testNode);\n        assertEquals(\"changed\", testNode.getDescription());\n    }\n\n    @Test\n    void testSetAccountSeparator() {\n        String newSep = \".\";\n\n        String oldSep = e.getAccountSeparator();\n        assertNotNull(oldSep);\n\n        e.setAccountSeparator(newSep);\n\n        assertEquals(e.getAccountSeparator(), newSep);\n\n        e.setAccountSeparator(oldSep);\n\n        assertEquals(e.getAccountSeparator(), oldSep);\n    }\n\n    @Test\n    void testGetAccountSeparator() {\n        String sep = e.getAccountSeparator();\n\n        assertNotNull(sep);\n        assertFalse(sep.isEmpty());\n    }\n\n    @Test\n    void testGetAccountByUuid() {\n        UUID rootUUID = e.getRootAccount().getUuid();\n\n        assertEquals(e.getRootAccount(), e.getAccountByUuid(rootUUID));\n    }\n\n    @Test\n    void testGetAccountByName() {\n        final String ACCOUNT_NAME = \"testAccount\";\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        e.addAccount(e.getRootAccount(), a);\n\n        assertEquals(a, e.getAccountByName(ACCOUNT_NAME));\n    }\n\n    @Test\n    void testGetIncomeAccountList() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.INCOME, node);\n        a.setName(\"Income\");\n        e.addAccount(e.getRootAccount(), a);\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertFalse(e.getIncomeAccountList().isEmpty());\n    }\n\n    @Test\n    void testGetExpenseAccountList() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.EXPENSE, node);\n        a.setName(\"Expense\");\n        e.addAccount(e.getRootAccount(), a);\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertFalse(e.getExpenseAccountList().isEmpty());\n    }\n\n    @Test\n    void testGetBankAccountList() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(\"Asset\");\n        e.addAccount(e.getRootAccount(), a);\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertNotNull(e.getAccountByName(\"Asset\"));\n    }\n\n    @Test\n    void testGetInvestmentAccountList() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.INVEST, node);\n        a.setName(\"Invest\");\n        e.addAccount(e.getRootAccount(), a);\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertFalse(e.getInvestmentAccountList().isEmpty());\n    }\n\n    @Test\n    void testAccounts() {\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        assertNotNull(node);\n\n        Account parent = e.getRootAccount();\n\n        assertNotNull(parent);\n\n        for (int i = 0; i < 50; i++) {\n            Account child = new Account(AccountType.BANK, node);\n            child.setName(\"Account\" + (i + 1));\n            child.setAccountNumber(Integer.toString(i + 1));\n\n            if (i % 2 == 0) {\n                child.setPlaceHolder(true);\n            }\n\n            e.addAccount(parent, child);\n\n            parent = child;\n        }\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        parent = e.getRootAccount();\n\n        // look for all the accounts\n        assertEquals(50, e.getAccountList().size());\n\n        for (int i = 0; i < 50; i++) {\n            assertEquals(1, parent.getChildCount());\n\n            if (parent.getChildCount() == 1) {\n                Account child = parent.getChildren().get(0);\n\n                assertEquals(\"Account\" + (i + 1), child.getName());\n                assertEquals(Integer.toString(i + 1), child.getAccountNumber());\n\n                if (i % 2 == 0) {\n                    assertTrue(child.isPlaceHolder());\n                } else {\n                    assertFalse(child.isPlaceHolder());\n                }\n\n                parent = child;\n            }\n        }\n\n    }\n\n    @Test\n    void testAccountAttributes() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(\"AccountAttributes\");\n\n        e.addAccount(e.getRootAccount(), a);\n        e.setAccountAttribute(a, \"myStuff\", \"gobbleDeGook\");\n        e.setAccountAttribute(a, \"myKey\", \"myValue\");\n        e.setAccountAttribute(a, \"myNumber\", BigDecimal.TEN.toString());\n\n        Account b = e.getAccountByUuid(a.getUuid());\n\n        assertEquals(\"gobbleDeGook\", b.getAttribute(\"myStuff\"));\n\n        // close and reopen to force check for persistence\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        b = e.getAccountByUuid(a.getUuid());\n        assertEquals(\"gobbleDeGook\", b.getAttribute(\"myStuff\"));\n        assertEquals(\"myValue\", b.getAttribute(\"myKey\"));\n\n        String attribute = b.getAttribute(\"myNumber\");\n\n        assertNotNull(attribute);\n\n        assertEquals(BigDecimal.TEN, new BigDecimal(attribute));\n    }\n\n    @Test\n    void testAddAccount() {\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(\"testAddAccount\");\n\n        assertTrue(e.addAccount(e.getRootAccount(), a));\n\n        assertTrue(e.isStored(a));\n    }\n\n    @Test\n    void testGetRootAccount() {\n        RootAccount root = e.getRootAccount();\n\n        assertNotNull(root);\n    }\n\n    @Test\n    void testRemoveAccount() {\n        final String ACCOUNT_NAME = \"testIsStored\";\n        CurrencyNode node = e.getDefaultCurrency();\n\n        assertNotNull(e);\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        assertFalse(e.isStored(a));\n\n        e.addAccount(e.getRootAccount(), a);\n\n        assertTrue(e.isStored(a));\n\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        a = e.getAccountByName(ACCOUNT_NAME);\n\n        assertTrue(e.removeAccount(a));\n\n        closeEngine();\n\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertNull(e.getAccountByName(ACCOUNT_NAME));\n    }\n\n    @Test\n    void testIsStored() {\n\n        final String ACCOUNT_NAME = \"testIsStored\";\n        CurrencyNode node = e.getDefaultCurrency();\n\n        assertNotNull(e);\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        assertFalse(e.isStored(a));\n\n        e.addAccount(e.getRootAccount(), a);\n\n        assertTrue(e.isStored(a));\n    }\n\n    @Test\n    void testAddGetRemoveReconcileSingleEntryTransactions() {\n        final String ACCOUNT_NAME = \"testAccount\";\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        e.addAccount(e.getRootAccount(), a);\n\n        e.addTransaction(TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(), \"memo1\",\n                \"payee1\", \"1\"));\n        e.addTransaction(TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(), \"memo2\",\n                \"payee2\", \"2\"));\n        e.addTransaction(TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(), \"memo3\",\n                \"payee3\", \"3\"));\n\n        assertEquals(3, a.getTransactionCount());\n\n        assertEquals(3, e.getTransactions().size());\n\n        a = e.getAccountByName(ACCOUNT_NAME);\n        assertEquals(3, a.getTransactionCount());\n\n        // Check for correct reconciliation\n        for (final Transaction transaction : e.getTransactions()) {\n            assertEquals(transaction.getReconciled(a), ReconciledState.NOT_RECONCILED);\n        }\n\n        for (final Transaction transaction : e.getTransactions()) {\n            e.setTransactionReconciled(transaction, a, ReconciledState.CLEARED);\n        }\n\n        for (final Transaction transaction : e.getTransactions()) {\n            assertEquals(transaction.getReconciled(a), ReconciledState.CLEARED);\n        }\n\n        for (final Transaction transaction : e.getTransactions()) {\n            e.setTransactionReconciled(transaction, a, ReconciledState.RECONCILED);\n        }\n\n        for (final Transaction transaction : e.getTransactions()) {\n            assertEquals(transaction.getReconciled(a), ReconciledState.RECONCILED);\n        }\n\n\n        for (int i = 2; i >= 0; i--) {\n            List<Transaction> transactions = e.getTransactions();\n            assertTrue(e.removeTransaction(transactions.get(0)));\n            assertEquals(i, e.getTransactions().size());\n        }\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        a = e.getAccountByName(ACCOUNT_NAME);\n        assertEquals(0, a.getTransactionCount());\n    }\n\n    @Test\n    void testGetTransactionsWithAttachments() {\n        final String ACCOUNT_NAME = \"testAccount\";\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account a = new Account(AccountType.BANK, node);\n        a.setName(ACCOUNT_NAME);\n\n        e.addAccount(e.getRootAccount(), a);\n\n        e.addTransaction(TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(), \"memo\",\n                \"payee\", \"1\"));\n\n        Transaction link = TransactionFactory.generateSingleEntryTransaction(a, BigDecimal.TEN, LocalDate.now(), \"memo\",\n                \"payee\", \"1\");\n        link.setAttachment(\"external link\");\n\n        e.addTransaction(link);\n\n        assertEquals(2, e.getTransactions().size());\n        assertEquals(1, e.getTransactionsWithAttachments().size());\n\n        // close and reopen to force check for persistence\n        closeEngine();\n        e = EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD);\n\n        assertEquals(2, e.getTransactions().size());\n        assertEquals(1, e.getTransactionsWithAttachments().size());\n    }\n\n    @Test\n    void testGetUuid() {\n        assertNotNull(e.getUuid());\n        assertFalse(e.getUuid().isEmpty());\n    }\n\n    @Test\n    void testVersion() {\n        try {\n            RootAccount account = e.getRootAccount();\n\n            Account temp = e.getStoredObjectByUuid(RootAccount.class, account.getUuid());\n            assertEquals(account, temp);\n\n            // close and reopen to force check for persistence\n            EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n            final float version = EngineFactory.getFileVersion(Paths.get(testFile), EngineFactory.EMPTY_PASSWORD);\n            final float engineVersion = Float.parseFloat(Engine.CURRENT_MAJOR_VERSION + \".\" + Engine.CURRENT_MINOR_VERSION);\n\n            assertEquals(version, engineVersion, DELTA);\n        } catch (final Exception e) {\n            fail(e.getMessage());\n        }\n    }\n\n    @Test\n    void testTransactionNumberList() {\n        List<String> numbers = e.getTransactionNumberList();\n\n        int size = numbers.size();\n\n        assertTrue(size > 0);\n\n        numbers.add(\"test 1\");\n        numbers.add(\"test 2\");\n\n        e.setTransactionNumberList(numbers);\n\n        List<String> numbers2 = e.getTransactionNumberList();\n\n        assertArrayEquals(numbers.toArray(), numbers2.toArray());\n\n        assertEquals((size + 2), numbers2.size());\n    }\n\n    @Test\n    void testAccountDepthAndComparator() {\n        CurrencyNode defaultCurrency = e.getDefaultCurrency();\n\n        final RootAccount root = e.getRootAccount();\n\n        final Account a = new Account(AccountType.BANK, defaultCurrency);\n        a.setName(\"a\");\n        e.addAccount(root, a);\n\n        final Account b = new Account(AccountType.BANK, defaultCurrency);\n        b.setName(\"b\");\n        e.addAccount(a, b);\n\n        final Account c2 = new Account(AccountType.BANK, defaultCurrency);\n        c2.setName(\"c2\");\n        e.addAccount(b, c2);\n\n        final Account c1 = new Account(AccountType.BANK, defaultCurrency);\n        c1.setName(\"c1\");\n        e.addAccount(b, c1);\n\n        final Account d = new Account(AccountType.BANK, defaultCurrency);\n        d.setName(\"d\");\n        e.addAccount(c1, d);\n\n        assertEquals(0, root.getDepth());\n        assertEquals(1, a.getDepth());\n        assertEquals(2, b.getDepth());\n        assertEquals(3, c1.getDepth());\n        assertEquals(3, c2.getDepth());\n        assertEquals(4, d.getDepth());\n\n        assertNotNull(AccountUtils.searchTree(root, \"d\", AccountType.BANK, 4));\n        assertNull(AccountUtils.searchTree(root, \"d\", AccountType.BANK, 3));\n\n        // checks the custom Comparator sort order function\n        final List<Account> accountList = e.getAccountList();\n\n        accountList.sort(Comparators.getAccountByTreePosition(Comparators.getAccountByCode()));\n\n        for (final Account account : accountList) {\n            System.out.println(account.getPathName());\n        }\n\n        assertEquals(a, accountList.get(0));\n        assertEquals(d, accountList.get(3));\n        assertEquals(c2, accountList.get(4));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/FileTransferTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.jpa.JpaH2DataStore;\nimport jgnash.engine.jpa.JpaHsqlDataStore;\nimport jgnash.engine.jpa.JpaNetworkServer;\nimport jgnash.engine.jpa.SqlUtils;\nimport jgnash.util.FileUtils;\n\nimport org.junit.jupiter.api.Test;\n\nimport io.netty.util.ResourceLeakDetector;\n\nimport static org.awaitility.Awaitility.await;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * File transfer test.\n *\n * @author Craig Cavanaugh\n */\nclass FileTransferTest {\n\n    @Test\n    void encryptedNetworkedTest() {\n\n        final int port = JpaNetworkServer.DEFAULT_PORT + 120;\n\n        final char[] password = new char[]{'p','a','s','s','w','o','r','d'};\n\n        //System.setProperty(EncryptionManager.ENCRYPTION_FLAG, \"true\");\n        //System.setProperty(\"ssl\", \"true\");\n\n        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);\n\n        String testFile = null;\n\n        try {\n            final Path temp = Files.createTempFile(\"jpa-test-e\", JpaH2DataStore.H2_FILE_EXT);\n            Files.delete(temp);\n\n            temp.toFile().deleteOnExit();\n            testFile = temp.toString();\n\n            assertNotNull(testFile);\n        } catch (final IOException e) {\n            Logger.getLogger(FileTransferTest.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            fail();\n        }\n\n        // Start an engine and close so we have a populated file\n        EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.H2_DATABASE);\n\n        EngineFactory.getEngine(EngineFactory.DEFAULT).setCreateBackups(false); // disable for test\n\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        // Change the password\n        SqlUtils.changePassword(testFile, EngineFactory.EMPTY_PASSWORD, password);\n\n        final JpaNetworkServer networkServer = new JpaNetworkServer();\n\n        final String serverFile = testFile;\n\n        Logger.getLogger(FileTransferTest.class.getName()).info(\"Starting Server\");\n\n        StartServerThread startServerThread = new StartServerThread(networkServer, serverFile,\n                port, password);\n\n        startServerThread.start();\n\n        // wait until the server is up and running\n        await().atMost(20, TimeUnit.SECONDS).untilTrue(startServerThread.running);\n\n        try {\n            Engine e = EngineFactory.bootClientEngine(EngineFactory.LOCALHOST, port,\n                    password, EngineFactory.DEFAULT);\n\n            Account account = new Account(AccountType.CASH, e.getDefaultCurrency());\n            account.setName(\"test\");\n            e.addAccount(e.getRootAccount(), account);\n\n            Path tempAttachment = Paths.get(FileTransferTest.class.getResource(\"/jgnash-logo.png\").toURI());\n            assertTrue(Files.exists(tempAttachment));\n\n            assertTrue(e.addAttachment(tempAttachment, true));  // push a copy of the attachment\n\n            Path newPath = Paths.get(AttachmentUtils.getAttachmentDirectory(Paths.get(testFile))\n                    + FileUtils.SEPARATOR + tempAttachment.getFileName());\n\n            newPath.toFile().deleteOnExit();\n\n            // wait for transfer to finish\n            await().atMost(10, TimeUnit.SECONDS).until(() -> Files.exists(newPath));\n\n            // Verify copy has occurred\n            assertEquals(tempAttachment.toFile().length(), newPath.toFile().length()); // same length?\n            assertNotEquals(tempAttachment.toString(), newPath.toString()); // different files?\n\n            final Path attachmentPath = AttachmentUtils.getAttachmentDirectory(Paths.get(testFile));\n            assertNotNull(attachmentPath);\n\n            // Create a new temp file in the directory\n            tempAttachment = Files.createTempFile(attachmentPath, \"tempfile2-\", \".txt\");\n            tempAttachment.toFile().deleteOnExit();\n\n            //write it\n            try (final BufferedWriter bw = Files.newBufferedWriter(tempAttachment, Charset.defaultCharset())) {\n                bw.write(\"This is the temporary file content 2.\");\n            }\n\n            Future<Path> pathFuture = e.getAttachment(tempAttachment.getFileName().toString());\n\n            Path remoteTemp = pathFuture.get();\n\n            assertTrue(Files.exists(remoteTemp));\n            assertNotEquals(remoteTemp.toString(), tempAttachment.toString());\n\n            EngineFactory.closeEngine(EngineFactory.DEFAULT);\n        } catch (final Exception e) {\n            Logger.getLogger(FileTransferTest.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            fail();\n        }\n\n    }\n\n    @Test\n    void networkedTest() {\n        final int port = JpaNetworkServer.DEFAULT_PORT + 110;\n\n        //System.setProperty(EncryptionManager.ENCRYPTION_FLAG, \"false\");\n        //System.setProperty(\"ssl\", \"false\");\n\n        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);\n\n        String testFile = null;\n\n        try {\n            Path temp = Files.createTempFile(\"jpa-test\", JpaHsqlDataStore.FILE_EXT);\n            Files.delete(temp);\n\n            temp.toFile().deleteOnExit();\n\n            testFile = temp.toString();\n\n            assertNotNull(testFile);\n        } catch (final IOException e) {\n            Logger.getLogger(FileTransferTest.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            fail();\n        }\n\n        // Start an engine and close so we have a populated file\n        EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.HSQL_DATABASE);\n\n        EngineFactory.getEngine(EngineFactory.DEFAULT).setCreateBackups(false); // disable for test\n\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        final JpaNetworkServer networkServer = new JpaNetworkServer();\n\n        final String serverFile = testFile;\n\n        Logger.getLogger(FileTransferTest.class.getName()).info(\"Starting Server\");\n\n        StartServerThread startServerThread = new StartServerThread(networkServer, serverFile, port,\n                EngineFactory.EMPTY_PASSWORD);\n\n        startServerThread.start();\n\n        // wait until the server is up and running\n        await().atMost(20, TimeUnit.SECONDS).untilTrue(startServerThread.running);\n\n        try {\n            Engine e = EngineFactory.bootClientEngine(EngineFactory.LOCALHOST, port, EngineFactory.EMPTY_PASSWORD,\n                    EngineFactory.DEFAULT);\n\n            Account account = new Account(AccountType.CASH, e.getDefaultCurrency());\n            account.setName(\"test\");\n            e.addAccount(e.getRootAccount(), account);\n\n            Path tempAttachment = Paths.get(FileTransferTest.class.getResource(\"/jgnash-logo.png\").toURI());\n            assertTrue(Files.exists(tempAttachment));\n\n            assertTrue(e.addAttachment(tempAttachment, true));  // push a copy of the attachment\n\n            final Path newPath = Paths.get(AttachmentUtils.getAttachmentDirectory(Paths.get(testFile))\n                    + FileUtils.SEPARATOR + tempAttachment.getFileName());\n\n            // wait for transfer to finish\n            await().atMost(10, TimeUnit.SECONDS).until(() -> Files.exists(newPath));\n\n            newPath.toFile().deleteOnExit();\n\n            // Verify copy has occurred\n            assertEquals(tempAttachment.toFile().length(), newPath.toFile().length()); // same length?\n            assertNotEquals(tempAttachment.toString(), newPath.toString()); // different files?\n\n            // Test that move is working\n            Path moveFile = Files.createTempFile(\"jgnash\", \"test\");\n            \n            try (final BufferedWriter bw = Files.newBufferedWriter(moveFile, Charset.defaultCharset())) {\n            \t bw.write(\"This is the temporary file content 3.\");\n            }\n                                 \n            assertTrue(e.addAttachment(moveFile, false));\n            assertFalse(Files.exists(moveFile));\n\n            final Path attachmentPath = AttachmentUtils.getAttachmentDirectory(Paths.get(testFile));\n            assertNotNull(attachmentPath);\n\n            // Create a new temp file in the directory\n            tempAttachment = Files.createTempFile(attachmentPath, \"tempfile2-\", \".txt\");\n            tempAttachment.toFile().deleteOnExit();\n            \n            try (final BufferedWriter bw = Files.newBufferedWriter(tempAttachment, Charset.defaultCharset())) {\n            \t bw.write(\"This is the temporary file content 2.\");\n            }         \n\n            Future<Path> pathFuture = e.getAttachment(tempAttachment.getFileName().toString());\n\n            Path remoteTemp = pathFuture.get();\n\n            assertTrue(Files.exists(remoteTemp));\n            assertNotEquals(remoteTemp.toString(), tempAttachment.toString());\n\n            // test attachment removal\n            assertTrue(e.removeAttachment(moveFile.getFileName().toString()));\n            assertFalse(Files.exists(moveFile));\n\n            EngineFactory.closeEngine(EngineFactory.DEFAULT);\n        } catch (Exception e) {\n            Logger.getLogger(FileTransferTest.class.getName()).log(Level.SEVERE, e.getLocalizedMessage(), e);\n            fail();\n        }\n\n    }\n\n    private static class StartServerThread extends Thread {\n\n        private final JpaNetworkServer networkServer;\n        private final String serverFile;\n        private final int port;\n        private final char[] password;\n\n        final AtomicBoolean running = new AtomicBoolean(false);\n\n        StartServerThread(JpaNetworkServer networkServer, String serverFile, int port, char[] password) {\n            this.networkServer = networkServer;\n            this.serverFile = serverFile;\n            this.port = port;\n            this.password = password;\n        }\n\n        @Override\n        public void run() {\n            networkServer.startServer(serverFile, port, password, () -> running.set(true));\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/InvestmentHistoryExchangeTest.java",
    "content": "package jgnash.engine;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.nio.file.Files;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\n\nimport static jgnash.engine.TransactionFactory.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n  * Unit test for investment history and exchange rate combinations.\n  *\n  * @author Craig Cavanaugh\n  */\nclass InvestmentHistoryExchangeTest {\n\n     private static final DateTimeFormatter SIMPLE_DATE_FORMAT = DateTimeFormatter.ofPattern(\"yyyy-MM-dd\");\n\n     private String database;\n\n     private Engine e;\n\n     private Account expenseAccount;\n\n     private Account usdBankAccount;\n\n     private Account investAccount;\n\n     private SecurityNode securityNode;\n\n     private CurrencyNode usdCurrency;\n\n     private CurrencyNode cadCurrency;\n\n     @Test\n     void testExchangeRate() {\n         assertEquals(new BigDecimal(\"0.5\"), usdCurrency.getExchangeRate(cadCurrency));\n         assertEquals(new BigDecimal(\"2\"), cadCurrency.getExchangeRate(usdCurrency));\n     }\n\n     private static LocalDate getLocalDate(final String date) {\n         return LocalDate.parse(date, SIMPLE_DATE_FORMAT);\n     }\n\n     @Test\n     void testHistorySearch() {\n\n         final SecurityHistoryNode old = new SecurityHistoryNode();\n         old.setDate(getLocalDate(\"2014-06-26\"));\n         old.setPrice(new BigDecimal(\"500.00\"));\n         assertTrue(e.addSecurityHistory(securityNode, old));\n\n         final SecurityHistoryNode today = new SecurityHistoryNode();\n         today.setDate(getLocalDate(\"2014-06-27\"));\n         today.setPrice(new BigDecimal(\"501.00\"));\n         assertTrue(e.addSecurityHistory(securityNode, today));\n\n         final SecurityHistoryNode future = new SecurityHistoryNode();\n         future.setDate(getLocalDate(\"2014-06-28\"));\n         future.setPrice(new BigDecimal(\"502.00\"));\n         assertTrue(e.addSecurityHistory(securityNode, future));\n\n         Optional<SecurityHistoryNode> search = securityNode.getClosestHistoryNode(getLocalDate(\"2014-06-26\"));\n         assertTrue(search.isPresent());\n         search.ifPresent(securityHistoryNode -> assertEquals(old, securityHistoryNode));\n\n         search = securityNode.getClosestHistoryNode(getLocalDate(\"2014-06-27\"));\n         assertTrue(search.isPresent());\n         search.ifPresent(securityHistoryNode -> assertEquals(today, securityHistoryNode));\n\n         search = securityNode.getClosestHistoryNode(getLocalDate(\"2014-06-28\"));\n         assertTrue(search.isPresent());\n         search.ifPresent(securityHistoryNode -> assertEquals(future, securityHistoryNode));\n\n         // postdate closest search, should return null\n         search = securityNode.getClosestHistoryNode(getLocalDate(\"2014-06-29\"));\n         assertTrue(search.isPresent());\n         search.ifPresent(securityHistoryNode -> assertEquals(future, securityHistoryNode));\n\n         // predate closest search, should return null\n         search = securityNode.getClosestHistoryNode(getLocalDate(\"2014-06-25\"));\n         assertFalse(search.isPresent());\n\n         // predate exact match, should turn null;\n         search = securityNode.getHistoryNode(getLocalDate(\"2014-06-25\"));\n         assertFalse(search.isPresent());\n\n         // postdate exact match, should turn null;\n         search = securityNode.getHistoryNode(getLocalDate(\"2014-06-29\"));\n         assertFalse(search.isPresent());\n\n         // exact match, should match\n         search = securityNode.getHistoryNode(getLocalDate(\"2014-06-27\"));\n         assertTrue(search.isPresent());\n         search.ifPresent(securityHistoryNode -> assertEquals(today, securityHistoryNode));\n\n         BigDecimal price = Engine.getMarketPrice(Collections.emptyList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-06-29\"));\n\n         assertEquals(new BigDecimal(\"502.00\"), price);\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, usdCurrency, getLocalDate(\"2014-06-28\"));\n         assertEquals(new BigDecimal(\"502.00\"), price);\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, usdCurrency, getLocalDate(\"2014-06-27\"));\n         assertEquals(new BigDecimal(\"501.00\"), price);\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, usdCurrency, getLocalDate(\"2014-06-26\"));\n         assertEquals(new BigDecimal(\"500.00\"), price);\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, usdCurrency, getLocalDate(\"2014-06-25\"));\n         assertEquals(BigDecimal.ZERO, price);\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, cadCurrency, getLocalDate(\"2014-06-25\"));\n         assertEquals(0, price.compareTo(BigDecimal.ZERO));\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, cadCurrency, getLocalDate(\"2014-06-26\"));\n         assertEquals(0, price.compareTo(new BigDecimal(\"250.00\")));\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, cadCurrency, getLocalDate(\"2014-06-27\"));\n         assertEquals(0, price.compareTo(new BigDecimal(\"250.50\")));\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, cadCurrency, getLocalDate(\"2014-06-28\"));\n         assertEquals(0, price.compareTo(new BigDecimal(\"251.00\")));\n\n         price = Engine.getMarketPrice(Collections.emptyList(), securityNode, cadCurrency, getLocalDate(\"2014-06-29\"));\n         assertEquals(0, price.compareTo(new BigDecimal(\"251.00\")));\n\n         /// Test with a transaction for history precedence ///\n\n         List<TransactionEntry> fees = new ArrayList<>();\n         fees.add(createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Fees\",\n                 TransactionTag.INVESTMENT_FEE));\n\n         // Buying shares\n         Transaction it = generateBuyXTransaction(usdBankAccount, investAccount, securityNode, new BigDecimal(\"501.34\"),\n                 new BigDecimal(\"125\"), BigDecimal.ONE, getLocalDate(\"2014-06-27\"), \"Buy shares\", fees);\n\n         assertTrue(e.addTransaction(it));\n\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-06-27\"));\n         assertEquals(new BigDecimal(\"501.00\"), price);\n\n         /// Test a transaction after any known security history ///\n\n         fees.clear();\n         fees.add(createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Fees\",\n                 TransactionTag.INVESTMENT_FEE));\n\n         it = generateBuyXTransaction(usdBankAccount, investAccount, securityNode, new BigDecimal(\"501.34\"),\n                 new BigDecimal(\"125\"), BigDecimal.ONE, getLocalDate(\"2014-06-29\"), \"Buy shares\", fees);\n\n         assertTrue(e.addTransaction(it));\n\n\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-06-29\"));\n\n         assertEquals(new BigDecimal(\"501.34\"), price);\n\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-06-30\"));\n\n         assertEquals(new BigDecimal(\"501.34\"), price);\n\n         /// Test a transaction after any known security history and between a newer ///\n\n         fees.clear();\n         fees.add(createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Fees\",\n                 TransactionTag.INVESTMENT_FEE));\n\n         it = generateBuyXTransaction(usdBankAccount, investAccount, securityNode, new BigDecimal(\"502.34\"),\n                 new BigDecimal(\"125\"), BigDecimal.ONE, getLocalDate(\"2014-07-01\"), \"Buy shares\", fees);\n\n         assertTrue(e.addTransaction(it));\n\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-07-01\"));\n\n         assertFalse(securityNode.getHistoryNode(getLocalDate(\"2014-07-01\")).isPresent());\n         assertEquals(new BigDecimal(\"502.34\"), price);\n\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-07-02\"));\n\n         assertFalse(securityNode.getHistoryNode(getLocalDate(\"2014-07-02\")).isPresent());\n         assertEquals(new BigDecimal(\"502.34\"), price);\n\n         assertFalse(securityNode.getHistoryNode(getLocalDate(\"2014-06-30\")).isPresent());\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-06-30\"));\n\n         assertEquals(new BigDecimal(\"501.34\"), price);\n\n         final SecurityHistoryNode future2 = new SecurityHistoryNode();\n         future2.setDate(getLocalDate(\"2014-07-02\"));\n         future2.setPrice(new BigDecimal(\"503.00\"));\n         assertTrue(e.addSecurityHistory(securityNode, future2));\n         assertTrue(securityNode.getHistoryNode(getLocalDate(\"2014-07-02\")).isPresent());\n\n         price = Engine.getMarketPrice(investAccount.getSortedTransactionList(), securityNode, usdCurrency,\n                 getLocalDate(\"2014-07-02\"));\n\n         assertEquals(new BigDecimal(\"503.00\"), price);\n\n     }\n\n     @BeforeEach\n     void setUp() {\n         try {\n             database = Files.createTempFile(\"jgnash\", \".bxds\").toString();\n             EngineFactory.deleteDatabase(database);\n\n             e = EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                     DataStoreType.BINARY_XSTREAM);\n             e.setCreateBackups(false);\n\n             // Creating currencies\n             usdCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n             e.addCurrency(usdCurrency);\n             e.setDefaultCurrency(usdCurrency);\n\n             cadCurrency = DefaultCurrencies.buildCustomNode(\"CAD\");\n             e.addCurrency(cadCurrency);\n\n             e.setExchangeRate(usdCurrency, cadCurrency, new BigDecimal(\"0.5\"), LocalDate.now().minusYears(10));\n\n             // Creating securities\n             securityNode = new SecurityNode(usdCurrency);\n\n             securityNode.setSymbol(\"GOOGL\");\n             securityNode.setDescription(\"Google\");\n             securityNode.setScale((byte) 2);\n             assertTrue(e.addSecurity(securityNode));\n\n             // Creating accounts\n             final Account incomeAccount = new Account(AccountType.INCOME, usdCurrency);\n             incomeAccount.setName(\"Income Account\");\n             e.addAccount(e.getRootAccount(), incomeAccount);\n\n             expenseAccount = new Account(AccountType.EXPENSE, usdCurrency);\n             expenseAccount.setName(\"Expense Account\");\n             e.addAccount(e.getRootAccount(), expenseAccount);\n\n             usdBankAccount = new Account(AccountType.BANK, usdCurrency);\n             usdBankAccount.setName(\"USD Bank Account\");\n             e.addAccount(e.getRootAccount(), usdBankAccount);\n\n             Account cadBankAccount = new Account(AccountType.BANK, cadCurrency);\n             cadBankAccount.setName(\"CAD Bank Account\");\n             e.addAccount(e.getRootAccount(), cadBankAccount);\n\n             Account equityAccount = new Account(AccountType.EQUITY, usdCurrency);\n             equityAccount.setName(\"Equity Account\");\n             e.addAccount(e.getRootAccount(), equityAccount);\n\n             Account liabilityAccount = new Account(AccountType.LIABILITY, usdCurrency);\n             liabilityAccount.setName(\"Liability Account\");\n             e.addAccount(e.getRootAccount(), liabilityAccount);\n\n             investAccount = new Account(AccountType.INVEST, usdCurrency);\n             investAccount.setName(\"Invest Account\");\n             e.addAccount(e.getRootAccount(), investAccount);\n\n             // Adding security to the invest account\n             List<SecurityNode> securityNodeList = new ArrayList<>();\n             securityNodeList.add(securityNode);\n             assertTrue(e.updateAccountSecurities(investAccount, securityNodeList));\n         } catch (final Exception e) {\n             fail(e.getMessage());\n         }\n     }\n\n     @AfterEach\n     void tearDown() {\n         EngineFactory.closeEngine(EngineFactory.DEFAULT);\n         EngineFactory.deleteDatabase(database);\n     }\n }\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/InvestmentPerformanceTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2021 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.Test;\n\nimport static jgnash.engine.TransactionFactory.createTransactionEntry;\nimport static jgnash.engine.TransactionFactory.generateBuyXTransaction;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * Unit tests for Investment Performance.\n *\n * @author Craig Cavanaugh\n */\npublic class InvestmentPerformanceTest extends AbstractEngineTest {\n\n    private final Logger logger = Logger.getLogger(InvestmentPerformanceSummary.class.getName());\n\n    InvestmentPerformanceTest() {\n    }\n\n    @Override\n    protected Engine createEngine() throws IOException {\n        database = testFolder.createFile(\"invest-perform-test.xml\").getAbsolutePath();\n\n        EngineFactory.deleteDatabase(database);\n\n        return EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.XML);\n    }\n\n    @Test\n    void basicInvestPerformance() {\n        final LocalDate startDate = LocalDate.of(2020, Month.JANUARY, 1);\n        final LocalDate endDate = LocalDate.of(2020, Month.DECEMBER, 30);\n\n        e.addTransaction(TransactionFactory.generateDoubleEntryTransaction(usdBankAccount, equityAccount,\n                new BigDecimal(\"500.00\"), startDate, \"Equity transaction\", \"\", \"\" ));\n\n        e.addTransaction(TransactionFactory.generateDoubleEntryTransaction(investAccount, usdBankAccount,\n                new BigDecimal(\"500.00\"), startDate, \"Move cash into investment account\", \"\", \"\" ));\n\n        InvestmentPerformanceSummary ips = new InvestmentPerformanceSummary(investAccount, startDate, endDate, true);\n\n        assertNotNull(ips);\n\n        ips.runCalculations();\n\n        InvestmentPerformanceSummary.SecurityPerformanceData spd = ips.getPerformanceData(gggSecurityNode);\n\n        logger.info(ips.toString());\n\n        final float delta = 0.01f;\n\n        assertEquals(0f, spd.getSharesHeld().floatValue(), delta);\n        assertEquals(0f, spd.getPrice().floatValue(), delta);\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(LocalDate.of(2020, Month.FEBRUARY, 1));\n        history.setPrice(new BigDecimal(\"10.0\"));\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        final List<TransactionEntry> fees = new ArrayList<>();\n        TransactionEntry fee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"30\"), \"Fee1\", TransactionTag.INVESTMENT_FEE);\n        fees.add(fee1);\n\n        // Buying shares\n        InvestmentTransaction it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode,\n                new BigDecimal(\"10.0\"), new BigDecimal(\"125\"), BigDecimal.ONE,\n                LocalDate.of(2020, Month.FEBRUARY, 2), \"Buy shares\", fees);\n\n        assertTrue(e.addTransaction(it));\n\n        ips = new InvestmentPerformanceSummary(investAccount, startDate, endDate, false);\n        ips.runCalculations();\n        logger.info(ips.toString());\n\n        spd = ips.getPerformanceData(gggSecurityNode);\n\n        assertEquals(125f, spd.getSharesHeld().floatValue(), delta);\n        assertEquals((1250f + 30f) / 125f, spd.getCostBasisPerShare().floatValue(), delta);\n        assertEquals(-30f, spd.getTotalGains().floatValue(), delta);\n        assertEquals(-30f, spd.getUnrealizedGains().floatValue(), delta);\n        assertEquals(1280f, spd.getTotalCostBasis().floatValue(), delta);\n        assertEquals(-30f / 1280f, spd.getTotalGainsPercentage().floatValue(), delta);\n\n\n\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/InvestmentTransactionTest.java",
    "content": "package jgnash.engine;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static jgnash.engine.TransactionFactory.*;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\n\n/**\n * Unit tests for investment account transactions.\n *\n * @author Peti\n * @author Craig Cavanaugh\n */\npublic class InvestmentTransactionTest extends AbstractEngineTest {\n\n    InvestmentTransactionTest() {\n    }\n\n    @Override\n    protected Engine createEngine() throws IOException {\n        database = testFolder.createFile(\"invest-transaction-test.xml\").getAbsolutePath();\n\n        EngineFactory.deleteDatabase(database);\n\n        return EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.XML);\n    }\n\n    @Test\n    void NoErrorIfSecurityHistoryEmpty() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        assertTrue(e.addTransaction(transaction));\n\n        // Adding securityPrice to the security price history\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n        BigDecimal securityPrice1;\n\n        securityPrice1 = new BigDecimal(\"2.00\");\n\n        // There is not any History added to the security\n\n        // Creating the list of fees\n        List<TransactionEntry> fees = new ArrayList<>();\n        TransactionEntry fee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Fee1\", TransactionTag.INVESTMENT_FEE);\n        fees.add(fee1);\n        TransactionEntry fee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Fee2\", TransactionTag.INVESTMENT_FEE);\n        fees.add(fee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", fees);\n\n        assertTrue(e.addTransaction(it));\n        assertFalse(e.addTransaction(it));\n    }\n\n    @Test\n    void BuyShares() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        assertTrue(e.addTransaction(transaction));\n\n        // Adding securityPrice to the security price history\n        BigDecimal securityPrice1;\n\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n\n        securityPrice1 = new BigDecimal(\"2.00\");\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of fees\n        List<TransactionEntry> fees = new ArrayList<>();\n        TransactionEntry fee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Fee1\", TransactionTag.INVESTMENT_FEE);\n        fees.add(fee1);\n        TransactionEntry fee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Fee2\", TransactionTag.INVESTMENT_FEE);\n        fees.add(fee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", fees);\n        assertTrue(e.addTransaction(it));\n\n        // Evaluating the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), investAccount.getBalance(),\n                investAccount.getMarketValue(), investAccount.getCashBalance()};\n        Object[] expected = {new BigDecimal(\"220.00\"), new BigDecimal(\"30.00\"), new BigDecimal(\"250.00\"),\n                new BigDecimal(\"250.00\"), new BigDecimal(\"0.00\")};\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void SellShares() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        e.addTransaction(transaction);\n\n        // Adding securityPrice to the security price history\n        //Date transactionDate1;\n        BigDecimal securityPrice1;\n\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n\n        securityPrice1 = new BigDecimal(\"2.00\");\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of buying fees\n        List<TransactionEntry> buyingFees = new ArrayList<>();\n        TransactionEntry bFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Buying Fee1\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee1);\n        TransactionEntry bFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Buying Fee2\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", buyingFees);\n        e.addTransaction(it);\n\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n        final BigDecimal securityPrice2 = new BigDecimal(\"3.00\");\n\n        history = new SecurityHistoryNode();\n        history.setDate(transactionDate2);\n        history.setPrice(securityPrice2);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of selling fees\n        List<TransactionEntry> sellingFees = new ArrayList<>();\n        TransactionEntry sFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Selling Fee1\", TransactionTag.INVESTMENT_FEE);\n        sellingFees.add(sFee1);\n        TransactionEntry sFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Selling Fee2\", TransactionTag.INVESTMENT_FEE);\n        sellingFees.add(sFee2);\n\n        // Creating the list of gains\n        List<TransactionEntry> sellingGains = new ArrayList<>();\n        TransactionEntry sGain1 = createTransactionEntry(incomeAccount, investAccount, new BigDecimal(\"20.00\"), \"Selling Gain1\", TransactionTag.GAIN_LOSS);\n        sellingGains.add(sGain1);\n        TransactionEntry sGain2 = createTransactionEntry(incomeAccount, investAccount, new BigDecimal(\"10.00\"), \"Selling Gain2\", TransactionTag.GAIN_LOSS);\n        sellingGains.add(sGain2);\n\n        it = generateSellXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice2, new BigDecimal(\"30\"), BigDecimal.ONE, transactionDate2, \"Selling shares\", sellingFees, sellingGains);\n        e.addTransaction(it);\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue(), investAccount.getCashBalance()};\n        Object[] expected = {new BigDecimal(\"280.00\"), new BigDecimal(\"60.00\"), new BigDecimal(\"-30.00\"),\n                new BigDecimal(\"285.00\"), new BigDecimal(\"285.00\"), new BigDecimal(\"0.00\")};\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void TransferCashIn() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n\n        e.addTransaction(TransactionFactory.generateDoubleEntryTransaction(usdBankAccount, equityAccount, new BigDecimal(\"500.00\"), transactionDate0, \"Equity transaction\", \"\", \"\" ));\n\n        // Adding cash in transaction\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n\n        e.addTransaction(TransactionFactory.generateDoubleEntryTransaction(investAccount, usdBankAccount, new BigDecimal(\"250.00\"), transactionDate1, \"Cash in transaction\", \"\", \"\"));\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue().setScale(2, RoundingMode.DOWN), investAccount.getCashBalance()};\n\n        Object[] expected = {new BigDecimal(\"250.00\"), BigDecimal.ZERO, BigDecimal.ZERO, new BigDecimal(\"250.00\"),\n                BigDecimal.ZERO.setScale(2, RoundingMode.DOWN), new BigDecimal(\"250.00\")};\n\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void TransferCashOut() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        e.addTransaction(transaction);\n\n        // Adding cash in transaction\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n\n        entry = new TransactionEntry();\n\n        entry.setDebitAccount(usdBankAccount);\n        entry.setDebitAmount(new BigDecimal(\"-250.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"250.00\"));\n        entry.setCreditAccount(investAccount);\n\n        entry.setMemo(\"Cash in transaction\");\n\n        transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate1);\n\n        e.addTransaction(transaction);\n\n        // Adding cash out transaction\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n\n        entry = new TransactionEntry();\n\n        entry.setDebitAccount(investAccount);\n        entry.setDebitAmount(new BigDecimal(\"-125.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"125.00\"));\n        entry.setCreditAccount(expenseAccount);\n\n        entry.setMemo(\"Cash out transaction\");\n\n        transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate2);\n\n        e.addTransaction(transaction);\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue().setScale(2, RoundingMode.DOWN),\n                investAccount.getCashBalance()};\n\n        Object[] expected = {new BigDecimal(\"250.00\"), new BigDecimal(\"125.00\"), BigDecimal.ZERO,\n                new BigDecimal(\"125.00\"), BigDecimal.ZERO.setScale(2, RoundingMode.DOWN), new BigDecimal(\"125.00\")};\n\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void Dividend() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        e.addTransaction(transaction);\n\n        // Adding securityPrice to the security price history\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n\n        BigDecimal securityPrice1;\n\n        securityPrice1 = new BigDecimal(\"2.00\");\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of buying fees\n        List<TransactionEntry> buyingFees = new ArrayList<>();\n        TransactionEntry bFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Buying Fee1\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee1);\n        TransactionEntry bFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Buying Fee2\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", buyingFees);\n        e.addTransaction(it);\n\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n\n        it = generateDividendXTransaction(incomeAccount, investAccount, usdBankAccount, gggSecurityNode, new BigDecimal(\"50.00\"), new BigDecimal(\"-50.00\"), new BigDecimal(\"50.00\"), transactionDate2, \"Dividend\");\n        e.addTransaction(it);\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue(), investAccount.getCashBalance()};\n        Object[] expected = {new BigDecimal(\"270.00\"), new BigDecimal(\"30.00\"), new BigDecimal(\"-50.00\"),\n                new BigDecimal(\"250.00\"), new BigDecimal(\"250.00\"), new BigDecimal(\"0.00\")};\n\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void ReinvestDividend() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        e.addTransaction(transaction);\n\n        // Adding securityPrice to the security price history\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n        BigDecimal securityPrice1;\n\n        securityPrice1 = new BigDecimal(\"2.00\");\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of buying fees\n        List<TransactionEntry> buyingFees = new ArrayList<>();\n        TransactionEntry bFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Buying Fee1\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee1);\n        TransactionEntry bFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Buying Fee2\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", buyingFees);\n        e.addTransaction(it);\n\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n\n        // Creating the list of selling fees\n        List<TransactionEntry> reinvestDividendFees = new ArrayList<>();\n        TransactionEntry rdFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"5.00\"), \"Reinvest Dividend Fee1\", TransactionTag.INVESTMENT_FEE);\n        reinvestDividendFees.add(rdFee1);\n        TransactionEntry rdFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"15.00\"), \"Reinvest Dividend Fee2\", TransactionTag.INVESTMENT_FEE);\n        reinvestDividendFees.add(rdFee2);\n\n        // Creating the list of gains\n        List<TransactionEntry> reinvestDividendGains = new ArrayList<>();\n        TransactionEntry rdGain1 = createTransactionEntry(incomeAccount, investAccount, new BigDecimal(\"20.00\"), \"Reinvest Dividend Gain1\", TransactionTag.GAIN_LOSS);\n        reinvestDividendGains.add(rdGain1);\n        TransactionEntry rdGain2 = createTransactionEntry(incomeAccount, investAccount, new BigDecimal(\"30.00\"), \"Reinvest Dividend Gain2\", TransactionTag.GAIN_LOSS);\n        reinvestDividendGains.add(rdGain2);\n\n        it = generateReinvestDividendXTransaction(investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"15\"), transactionDate2, \"Reinvest Dividend\", reinvestDividendFees, reinvestDividendGains);\n        e.addTransaction(it);\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue(), investAccount.getCashBalance()};\n\n        Object[] expected = {new BigDecimal(\"220.00\"), new BigDecimal(\"50.00\"), new BigDecimal(\"-50.00\"),\n                new BigDecimal(\"280.00\"), new BigDecimal(\"280.00\"), new BigDecimal(\"0.00\")};\n\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void StockSplit() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        e.addTransaction(transaction);\n\n        // Adding securityPrice to the security price history\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n        final BigDecimal securityPrice1 = new BigDecimal(\"2.00\");\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of buying fees\n        List<TransactionEntry> buyingFees = new ArrayList<>();\n        TransactionEntry bFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Buying Fee1\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee1);\n        TransactionEntry bFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Buying Fee2\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", buyingFees);\n        e.addTransaction(it);\n\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n\n        it = generateSplitXTransaction(investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), transactionDate2, \"Selling shares\");\n        e.addTransaction(it);\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue(), investAccount.getCashBalance()};\n\n        Object[] expected = {new BigDecimal(\"220.00\"), new BigDecimal(\"30.00\"), BigDecimal.ZERO,\n                new BigDecimal(\"500.00\"), new BigDecimal(\"500.00\"), new BigDecimal(\"0.00\")};\n\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n\n    @Test\n    void StockMerge() {\n        // Transferring some money to usdBankAccount\n        final LocalDate transactionDate0 = LocalDate.of(2009, Month.DECEMBER, 25);\n        TransactionEntry entry = new TransactionEntry();\n\n        entry.setDebitAccount(equityAccount);\n        entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n        entry.setCreditAmount(new BigDecimal(\"500.00\"));\n        entry.setCreditAccount(usdBankAccount);\n\n        entry.setMemo(\"Equity transaction\");\n\n        Transaction transaction = new Transaction();\n        transaction.addTransactionEntry(entry);\n        transaction.setDate(transactionDate0);\n\n        e.addTransaction(transaction);\n\n        // Adding securityPrice to the security price history\n        final LocalDate transactionDate1 = LocalDate.of(2009, Month.DECEMBER, 26);\n        final BigDecimal securityPrice1 = new BigDecimal(\"2.00\");\n\n        SecurityHistoryNode history = new SecurityHistoryNode();\n        history.setDate(transactionDate1);\n        history.setPrice(securityPrice1);\n\n        assertTrue(e.addSecurityHistory(gggSecurityNode, history));\n\n        // Creating the list of buying fees\n        List<TransactionEntry> buyingFees = new ArrayList<>();\n        TransactionEntry bFee1 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"20.00\"), \"Buying Fee1\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee1);\n        TransactionEntry bFee2 = createTransactionEntry(investAccount, expenseAccount, new BigDecimal(\"10.00\"), \"Buying Fee2\", TransactionTag.INVESTMENT_FEE);\n        buyingFees.add(bFee2);\n\n        // Buying shares\n        InvestmentTransaction it;\n        it = generateBuyXTransaction(usdBankAccount, investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"125\"), BigDecimal.ONE, transactionDate1, \"Buy shares\", buyingFees);\n        e.addTransaction(it);\n\n        final LocalDate transactionDate2 = LocalDate.of(2009, Month.DECEMBER, 27);\n\n        it = generateMergeXTransaction(investAccount, gggSecurityNode, securityPrice1, new BigDecimal(\"25\"), transactionDate2, \"Stock merge\");\n        e.addTransaction(it);\n\n        // Checking the result\n        Object[] actual = {usdBankAccount.getBalance(), expenseAccount.getBalance(), incomeAccount.getBalance(),\n                investAccount.getBalance(), investAccount.getMarketValue(), investAccount.getCashBalance()};\n\n        Object[] expected = {new BigDecimal(\"220.00\"), new BigDecimal(\"30.00\"), BigDecimal.ZERO,\n                new BigDecimal(\"200.00\"), new BigDecimal(\"200.00\"), new BigDecimal(\"0.00\")};\n\n        assertArrayEquals(expected, actual, \"Account balances are not as expected!\");\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/JpaH2EngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.jpa.JpaH2DataStore;\nimport jgnash.engine.jpa.SqlUtils;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * H2 Relational database engine test.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaH2EngineTest extends EngineTest {\n\n    @Override\n    public Engine createEngine() {\n        testFile = \"jpa-test\" + JpaH2DataStore.H2_FILE_EXT;\n\n        try {\n            testFile = Files.createTempFile(\"jpa-test\", JpaH2DataStore.H2_FILE_EXT).toString();\n\n        } catch (final IOException ex) {\n            Logger.getLogger(JpaH2EngineTest.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n            fail();\n        }\n\n        assertTrue(EngineFactory.deleteDatabase(testFile));\n\n        try {\n            return EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.H2_DATABASE);\n        } catch (final Exception e) {\n            fail(e.getMessage());\n            return null;\n        }\n    }\n\n    @Test\n    void dumpTableAndColumnNames() {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        final Set<String> tableNames = SqlUtils.getTableAndColumnNames(testFile);\n\n        tableNames.forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/JpaH2MvEngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.jpa.JpaH2MvDataStore;\nimport jgnash.engine.jpa.SqlUtils;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * H2 Relational database engine test.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaH2MvEngineTest extends EngineTest {\n\n\n    @Override\n    public Engine createEngine() {\n        testFile = \"jpa-testmv\" + JpaH2MvDataStore.MV_FILE_EXT;\n\n        try {\n            testFile = Files.createTempFile(\"jpa-testmv\", JpaH2MvDataStore.MV_FILE_EXT).toString();\n\n        } catch (final IOException ex) {\n            Logger.getLogger(JpaH2MvEngineTest.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n            fail();\n        }\n\n        assertTrue(EngineFactory.deleteDatabase(testFile));\n\n        try {\n            return EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.H2MV_DATABASE);\n        } catch (final Exception e) {\n            fail(e.getMessage());\n            return null;\n        }\n    }\n\n    @Test\n    void dumpTableAndColumnNames() {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        final Set<String> tableNames = SqlUtils.getTableAndColumnNames(testFile);\n\n        tableNames.forEach(System.out::println);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/JpaHsqlEngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport jgnash.engine.jpa.JpaHsqlDataStore;\nimport jgnash.engine.jpa.SqlUtils;\nimport jgnash.util.FileUtils;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * HSQLDB Relational database engine test.\n *\n * @author Craig Cavanaugh\n */\npublic class JpaHsqlEngineTest extends EngineTest {\n\n    private static String base;\n\n    @Override\n    public Engine createEngine() throws Exception {\n        testFile = \"jpa-test.\" + JpaHsqlDataStore.FILE_EXT;\n\n        try {\n            testFile = Files.createTempFile(\"jpa-test\", JpaHsqlDataStore.FILE_EXT).toString();\n\n            base = FileUtils.stripFileExtension(testFile);\n        } catch (IOException ex) {\n            Logger.getLogger(JpaHsqlEngineTest.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);\n            fail();\n        }\n\n        Files.delete(Paths.get(testFile));\n\n        try {\n            return EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.HSQL_DATABASE);\n        } catch (final Exception e) {\n            fail(e.getMessage());\n            return null;\n        }\n    }\n\n    @Test\n    void dumpTableAndColumnNames() {\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        Set<String> tableNames = SqlUtils.getTableAndColumnNames(testFile);\n\n        tableNames.forEach(System.out::println);\n    }\n\n    @AfterAll\n    static void cleanup() throws IOException {\n        Files.deleteIfExists(Paths.get(base + \".properties\"));\n        Files.deleteIfExists(Paths.get(base + \".lobs\"));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/PriorityThreadTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport jgnash.engine.concurrent.Priority;\nimport jgnash.engine.concurrent.PriorityThreadPoolExecutor;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * @author Craig Cavanaugh\n */\nclass PriorityThreadTest {\n\n    private static final int FUTURES = 25;   // 25 futures\n    private static final int DELAY_MILLIS = 500;\n\n    @Test\n    void testPriorityThreadPoolExecutor() throws Exception {\n\n        final AtomicLong atomicLongSequence = new AtomicLong(0);\n\n        final PriorityThreadPoolExecutor executorService = new PriorityThreadPoolExecutor();\n\n        final List<Future<Void>> futures = new ArrayList<>();\n\n        for (int i = 0; i < FUTURES; i++) {\n            Future<Void> future = executorService.submit(() -> {\n                final long value = atomicLongSequence.incrementAndGet();\n\n                Thread.sleep(DELAY_MILLIS);\n                System.out.println(\"Background Callable: \" + value);\n                return null;\n            }, Priority.BACKGROUND);\n\n            futures.add(future);\n        }\n\n        Thread.sleep(1999);\n\n        final Future<Void> priorityFuture = executorService.submit(() -> {\n            final long value = atomicLongSequence.incrementAndGet();\n\n            Thread.sleep(DELAY_MILLIS);\n            System.out.println(\"System Callable: \" + value);\n\n            assertTrue(value < (FUTURES / 2));    // should not be the last value\n            return null;\n        });\n\n        futures.add(priorityFuture);\n\n        // wait for futures to complete\n        for (Future<Void> future : futures) {\n            future.get();\n        }\n\n        executorService.shutdown();\n\n        executorService.shutdownNow();\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/TransactionTest.java",
    "content": "package jgnash.engine;\n\nimport io.github.glytching.junit.extension.folder.TemporaryFolder;\nimport io.github.glytching.junit.extension.folder.TemporaryFolderExtension;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass TransactionTest {\n\n    @Test\n    @ExtendWith(TemporaryFolderExtension.class)\n    void testBackEnd(final TemporaryFolder testFolder) throws IOException {\n\n        assertNotNull(testFolder);\n\n        final String database = testFolder.createFile(\"transaction-test.xml\").getAbsolutePath();\n\n        EngineFactory.deleteDatabase(database);\n\n        try {\n            Engine e = EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.XML);\n\n            e.setCreateBackups(false);\n\n            CurrencyNode defaultCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n            e.addCurrency(defaultCurrency);\n            e.setDefaultCurrency(defaultCurrency);\n\n            CurrencyNode cadCurrency = DefaultCurrencies.buildCustomNode(\"CAD\");\n            e.addCurrency(cadCurrency);\n\n            Account incomeAccount = new Account(AccountType.INCOME, defaultCurrency);\n            incomeAccount.setName(\"Income Account\");\n            e.addAccount(e.getRootAccount(), incomeAccount);\n\n            Account expenseAccount = new Account(AccountType.EXPENSE, defaultCurrency);\n            expenseAccount.setName(\"Expense Account\");\n            e.addAccount(e.getRootAccount(), expenseAccount);\n\n            Account usdBankAccount = new Account(AccountType.BANK, defaultCurrency);\n            usdBankAccount.setName(\"USD Bank Account\");\n            e.addAccount(e.getRootAccount(), usdBankAccount);\n\n            Account cadBankAccount = new Account(AccountType.BANK, cadCurrency);\n            cadBankAccount.setName(\"CAD Bank Account\");\n            e.addAccount(e.getRootAccount(), cadBankAccount);\n\n            TransactionEntry entry = new TransactionEntry();\n\n            entry.setDebitAccount(incomeAccount);\n            entry.setDebitAmount(new BigDecimal(\"-500.00\"));\n\n            entry.setCreditAmount(new BigDecimal(\"500.00\"));\n            entry.setCreditAccount(usdBankAccount);\n\n            entry.setMemo(\"Income transaction\");\n\n            Transaction transaction = new Transaction();\n            transaction.addTransactionEntry(entry);\n            transaction.setPayee(\"Employer\");\n\n            e.addTransaction(transaction);\n\n            assertEquals(new BigDecimal(\"500.00\"), transaction.getAmount(usdBankAccount));\n            assertEquals(new BigDecimal(\"500.00\"), usdBankAccount.getBalance());\n\n            EngineFactory.closeEngine(EngineFactory.DEFAULT);\n        } catch (final Exception e) {\n            fail(e.getMessage());\n        }\n    }\n\n    @Test\n    @ExtendWith(TemporaryFolderExtension.class)\n    void testEmptyAccount(final TemporaryFolder testFolder) throws IOException {\n        final String database = testFolder.createFile(\"empty-test.xml\").getAbsolutePath();\n\n        EngineFactory.deleteDatabase(database);\n\n        try {\n            Engine e = EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                    DataStoreType.XML);\n\n            e.setCreateBackups(false);\n\n            CurrencyNode defaultCurrency = DefaultCurrencies.buildCustomNode(\"USD\");\n\n            e.addCurrency(defaultCurrency);\n            e.setDefaultCurrency(defaultCurrency);\n\n            Account usdBankAccount = new Account(AccountType.BANK, defaultCurrency);\n            usdBankAccount.setName(\"USD Bank Account\");\n            e.addAccount(e.getRootAccount(), usdBankAccount);\n\n            // ensure API does not break down\n\n            List<Transaction> transactions = usdBankAccount.getSortedTransactionList();\n\n            assertTrue(transactions.isEmpty());\n\n            transactions = usdBankAccount.getTransactions(LocalDate.now().minusDays(1), LocalDate.now());\n\n            assertTrue(transactions.isEmpty());\n        } catch (final Exception e) {\n            fail(e.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/XMLEngineTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.AfterAll;\n\n/**\n * JUnit test for XML storage with the Engine API.\n *\n * @author Craig Cavanaugh\n */\npublic class XMLEngineTest extends EngineTest {\n\n    private static String tempFile;\n\n    @Override\n    public Engine createEngine() {\n        try {\n            testFile = Files.createTempFile(\"test\", DataStoreType.XML.getDataStore().getFileExt()).toString();\n            tempFile = testFile;\n\n        } catch (IOException e1) {\n            Logger.getLogger(XMLEngineTest.class.getName()).log(Level.SEVERE, e1.getLocalizedMessage(), e1);\n        }\n\n        EngineFactory.deleteDatabase(testFile);\n\n        return EngineFactory.bootLocalEngine(testFile, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.XML);\n    }\n\n    @AfterAll\n    static void cleanup() throws IOException {\n        Files.deleteIfExists(Paths.get(tempFile));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/net/security/IEXParserTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.net.security;\n\nimport java.io.IOException;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.List;\nimport java.util.Set;\n\nimport jgnash.engine.AbstractEngineTest;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.QuoteSource;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.net.security.SecurityParser;\nimport jgnash.net.security.iex.IEXParser;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;\n\nimport static jgnash.engine.net.security.YahooEventParserTest.GITHUB_ACTION;\nimport static org.hamcrest.CoreMatchers.instanceOf;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n/**\n * For these tests to work, the environment variable TEST_TOKEN must be set to the sandbox key\n */\npublic class IEXParserTest  extends AbstractEngineTest {\n\n    private static final String TEST_TOKEN = \"TEST_TOKEN\";\n\n    @Override\n    protected Engine createEngine() throws IOException {\n\n        database = testFolder.createFile(\"iex-test.bxds\").getAbsolutePath();\n        EngineFactory.deleteDatabase(database);\n\n        return EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.BINARY_XSTREAM);\n    }\n\n    @Test\n    @DisabledIfEnvironmentVariable(named = \"CI\", matches = \"true\")  // disable on Travis-CI\n    void testHistoricalDownload() throws IOException {\n\n        if (System.getenv(GITHUB_ACTION) != null) {   // don't test with Github actions\n            return;\n        }\n\n        // test env must be configured with a valid token\n        if (System.getenv(TEST_TOKEN) == null) {   // don't test with Github actions\n            return;\n        }\n\n        final SecurityNode ibm = new SecurityNode(e.getDefaultCurrency());\n        ibm.setSymbol(\"IBM\");\n        ibm.setScale((byte) 2);\n        ibm.setQuoteSource(QuoteSource.IEX_CLOUD);\n\n        e.addSecurity(ibm);\n\n        final QuoteSource quoteSource = ibm.getQuoteSource();\n        assertNotNull(quoteSource);\n\n        final SecurityParser securityParser = quoteSource.getParser();\n        assertNotNull(securityParser);\n\n        assertThat(securityParser, instanceOf(IEXParser.class));\n\n        ((IEXParser)securityParser).setUseSandbox();\n        securityParser.setTokenSupplier(() -> System.getenv(TEST_TOKEN));\n\n        final List<SecurityHistoryNode> events =  securityParser.retrieveHistoricalPrice(ibm, LocalDate.of(2019,\n                                                                 Month.JANUARY, 2), LocalDate.of(2019,\n                                                                 Month.MARCH, 1));\n\n        assertNotNull(events);\n        assertEquals(41, events.size());\n    }\n\n    @Test\n    @DisabledIfEnvironmentVariable(named = \"CI\", matches = \"true\")  // disable on Travis-CI\n    void testHistoricalSplitsDownload() throws IOException {\n\n        if (System.getenv(GITHUB_ACTION) != null) {   // don't test with Github actions\n            return;\n        }\n\n        // test env must be configured with a valid token\n       if (System.getenv(TEST_TOKEN) == null) {   // don't test with Github actions\n            return;\n        }\n\n        final SecurityNode pstv = new SecurityNode(e.getDefaultCurrency());\n        pstv.setSymbol(\"PSTV\");\n        pstv.setScale((byte) 2);\n        pstv.setQuoteSource(QuoteSource.IEX_CLOUD);\n\n        e.addSecurity(pstv);\n\n        final QuoteSource quoteSource = pstv.getQuoteSource();\n        assertNotNull(quoteSource);\n\n        final SecurityParser securityParser = quoteSource.getParser();\n        assertNotNull(securityParser);\n\n        assertThat(securityParser, instanceOf(IEXParser.class));\n\n        ((IEXParser)securityParser).setUseSandbox();\n        securityParser.setTokenSupplier(() -> System.getenv(TEST_TOKEN));\n\n        final Set<SecurityHistoryEvent> historicalEvents = securityParser.retrieveHistoricalEvents(pstv,\n                LocalDate.of(2015, Month.JANUARY, 1));\n\n        assertNotNull(historicalEvents);\n        assertEquals(1, historicalEvents.size());\n    }\n\n    @Test\n    @DisabledIfEnvironmentVariable(named = \"CI\", matches = \"true\")  // disable on Travis-CI\n    void testHistoricalDividendsDownload() throws IOException {\n\n        if (System.getenv(GITHUB_ACTION) != null) {   // don't test with Github actions\n            return;\n        }\n\n        // test env must be configured with a valid token\n        if (System.getenv(TEST_TOKEN) == null) {   // don't test with Github actions\n            return;\n        }\n\n        final SecurityNode spy = new SecurityNode(e.getDefaultCurrency());\n        spy.setSymbol(\"SPY\");\n        spy.setScale((byte) 2);\n        spy.setQuoteSource(QuoteSource.IEX_CLOUD);\n\n        e.addSecurity(spy);\n\n        final QuoteSource quoteSource = spy.getQuoteSource();\n        assertNotNull(quoteSource);\n\n        final SecurityParser securityParser = quoteSource.getParser();\n        assertNotNull(securityParser);\n\n        assertThat(securityParser, instanceOf(IEXParser.class));\n\n        ((IEXParser)securityParser).setUseSandbox();\n        securityParser.setTokenSupplier(() -> System.getenv(TEST_TOKEN));\n\n        final Set<SecurityHistoryEvent> historicalEvents = securityParser.retrieveHistoricalEvents(spy,\n                LocalDate.of(2015, Month.JANUARY, 1));\n\n        assertNotNull(historicalEvents);\n        assertEquals(1, historicalEvents.size());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/net/security/YahooEventParserTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.net.security;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport jgnash.engine.AbstractEngineTest;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.QuoteSource;\nimport jgnash.engine.SecurityHistoryEvent;\nimport jgnash.engine.SecurityHistoryNode;\nimport jgnash.engine.SecurityNode;\nimport jgnash.net.YahooCrumbManager;\nimport jgnash.net.security.SecurityParser;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n/**\n * JUnit test for the Yahoo security downloader.\n *\n * @author Craig Cavanaugh\n */\npublic class YahooEventParserTest extends AbstractEngineTest {\n\n    private static final int ATTEMPTS = 3;\n    public static final String GITHUB_ACTION = \"GITHUB_ACTION\";\n\n    @Override\n    protected Engine createEngine() throws IOException {\n\n        database = testFolder.createFile(\"yahoo-test.bxds\").getAbsolutePath();\n        EngineFactory.deleteDatabase(database);\n\n        return EngineFactory.bootLocalEngine(database, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.BINARY_XSTREAM);\n    }\n\n    @SuppressWarnings(\"ConstantConditions\")\n    @Test\n    @DisabledIfEnvironmentVariable(named = \"CI\", matches = \"true\")  // disable on Travis-CI\n    @Disabled\n    void testParser() throws IOException {\n\n        if (System.getenv(GITHUB_ACTION) != null) {   // don't test with Github actions\n           return;\n        }\n\n        for (int i = 0; i < ATTEMPTS; i++) {     // try multiple times to pass\n            final SecurityNode ibm = new SecurityNode(e.getDefaultCurrency());\n            ibm.setSymbol(\"IBM\");\n            ibm.setScale((byte) 2);\n            ibm.setQuoteSource(QuoteSource.YAHOO);\n\n            final SecurityHistoryNode historyNode = new SecurityHistoryNode(LocalDate.of(1962, Month.JANUARY, 1),\n                    BigDecimal.TEN, 1000, BigDecimal.TEN, BigDecimal.TEN);\n\n            e.addSecurity(ibm);\n            e.addSecurityHistory(ibm, historyNode);\n\n            QuoteSource quoteSource = ibm.getQuoteSource();\n            Objects.requireNonNull(quoteSource);\n\n            SecurityParser securityParser = quoteSource.getParser();\n            Objects.requireNonNull(securityParser);\n\n            final Set<SecurityHistoryEvent> events = securityParser.retrieveHistoricalEvents(ibm, LocalDate.of(2015, Month.AUGUST, 22));\n\n            assertNotNull(events);\n\n            // size fluctuates\n            if (events.size() <= 221 && events.size() >= 220) {\n                assertTrue(events.size() <= 221 && events.size() >= 220);\n                return;\n            }\n        }\n\n        fail(\"Failed to pass test\");\n    }\n\n    @Test\n    @DisabledIfEnvironmentVariable(named = \"CI\", matches = \"true\")  // disable on Travis-CI\n    @Disabled\n    void testHistoricalDownload() throws IOException {\n\n        if (System.getenv(GITHUB_ACTION) != null) {   // don't test with Github actions\n            return;\n        }\n\n        for (int i = 0; i < ATTEMPTS; i++) {      // try multiple times to pass\n            final SecurityNode ibm = new SecurityNode(e.getDefaultCurrency());\n            ibm.setSymbol(\"IBM\");\n            ibm.setScale((byte) 2);\n\n            e.addSecurity(ibm);\n\n            YahooCrumbManager.clearAuthorization();     // force re-authorization to prevent failed unit test\n\n            final List<SecurityHistoryNode> events = Objects.requireNonNull(QuoteSource.YAHOO.getParser())\n                                                             .retrieveHistoricalPrice(ibm, LocalDate.of(2016,\n                    Month.JANUARY, 1), LocalDate.of(2016, Month.DECEMBER, 30));\n\n            if (events.size() == 252) {\n                assertEquals(252, events.size());\n                return;\n            }\n        }\n\n        fail(\"Failed to pass test\");\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/recurring/DailyReminderTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.time.Month;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\n/**\n * Unit test for DailyReminders.\n *\n * @author Craig Cavanaugh\n */\nclass DailyReminderTest {\n\n    @Test\n    void simpleTest() {\n        final DailyReminder reminder = new DailyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.AUGUST, 28);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(LocalDate.of(2015, Month.SEPTEMBER, 3));\n\n        assertEquals(reminder.getReminderType(), ReminderType.DAILY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 29), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 30), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 31), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 1), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 2), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 3), iterator.next());\n        assertNull(iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 29), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 30), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 31), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 1), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 2), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 3), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertNull(reminder.getIterator().next());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/recurring/MonthlyReminderTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.util.Locale;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\n/**\n * Unit test for Monthly Reminders.\n *\n * @author Craig Cavanaugh\n */\nclass MonthlyReminderTest {\n\n    @Test\n    void iteratorDateTestOne() {\n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n        reminder.setType(MonthlyReminder.DATE);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 4), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.NOVEMBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.DECEMBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 4), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 4), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 4), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorDateTestTwo() {\n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(2);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n        reminder.setType(MonthlyReminder.DATE);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.NOVEMBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 4), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 4), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.NOVEMBER, 4), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorDateTestThree() {\n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(3);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n        reminder.setType(MonthlyReminder.DATE);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 4), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 4), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 4), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2016,Month.JANUARY, 4), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorDayTestOne() {\n        Locale.setDefault(Locale.US);      // how weeks are counted depends on the locale\n        \n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n        reminder.setType(MonthlyReminder.DAY);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 1), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 5), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 3), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.NOVEMBER, 7), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.DECEMBER, 5), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 2), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 1), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 5), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorDayTestTwo() {\n        Locale.setDefault(Locale.US);      // how weeks are counted depends on the locale\n        \n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(2);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n        reminder.setType(MonthlyReminder.DAY);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 5), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.NOVEMBER, 7), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 2), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.SEPTEMBER, 5), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.NOVEMBER, 7), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorDayTestThree() {\n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(3);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n        reminder.setType(MonthlyReminder.DAY);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 3), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 2), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 3), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 2), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2016, Month.APRIL, 2), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorDayTestFour() {\n        final MonthlyReminder reminder = new MonthlyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(3);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(LocalDate.of(2016, Month.APRIL, 3));\n        reminder.setType(MonthlyReminder.DAY);\n\n        assertEquals(reminder.getReminderType(), ReminderType.MONTHLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 3), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 2), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.OCTOBER, 3), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2016, Month.JANUARY, 2), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2016, Month.APRIL, 2), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertNull(reminder.getIterator().next());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/recurring/WeeklyReminderTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.time.Month;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\n/**\n * JUnit test for weekly reminders.\n *\n * @author Craig Cavanaugh\n */\nclass WeeklyReminderTest {\n\n    @Test\n    void iteratorTestOne() {\n        final WeeklyReminder reminder = new WeeklyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n\n        assertEquals(reminder.getReminderType(), ReminderType.WEEKLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 11), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 18), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 1), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 8), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.JULY, 11), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.JULY, 18), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorTestTwo() {\n        final WeeklyReminder reminder = new WeeklyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(2);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n\n        assertEquals(reminder.getReminderType(), ReminderType.WEEKLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 18), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 1), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.JULY, 18), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 1), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorTestThree() {\n        final WeeklyReminder reminder = new WeeklyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(3);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n\n        assertEquals(reminder.getReminderType(), ReminderType.WEEKLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 15), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 15), reminder.getIterator().next());\n    }\n\n    @Test\n    void iteratorTestFour() {\n        WeeklyReminder reminder = new WeeklyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(3);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(LocalDate.of(2015, Month.AUGUST, 15));\n\n        assertEquals(reminder.getReminderType(), ReminderType.WEEKLY);\n\n        RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), iterator.next());\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 15), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.AUGUST, 15), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertNull(reminder.getIterator().next());\n    }\n\n    // End date check\n    @Test\n    void iteratorTestFive() {\n        WeeklyReminder reminder = new WeeklyReminder();\n\n        final LocalDate startDate = LocalDate.of(2015, Month.JULY, 4);\n\n        reminder.setIncrement(3);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(LocalDate.of(2015, Month.AUGUST, 14));\n\n        assertEquals(reminder.getReminderType(), ReminderType.WEEKLY);\n\n        RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), iterator.next());\n        assertNull(iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2015, Month.JULY, 25), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertNull(reminder.getIterator().next());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/engine/recurring/YearlyReminderTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.engine.recurring;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.time.LocalDate;\nimport java.time.Month;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\n/**\n * Unit test for Yearly Reminders.\n *\n * @author Craig Cavanaugh\n */\nclass YearlyReminderTest {\n\n    @Test\n    void yearEndTestOne() {\n        final YearlyReminder reminder = new YearlyReminder();\n\n        final LocalDate startDate = LocalDate.ofYearDay(2011, 365);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n\n        assertEquals(reminder.getReminderType(), ReminderType.YEARLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.ofYearDay(2012, 366), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2013, 365), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2014, 365), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2015, 365), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2016, 366), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2017, 365), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2012, 366), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2013, 365), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2014, 365), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2015, 365), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2016, 366), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2017, 365), reminder.getIterator().next());\n    }\n\n    @Test\n    void yearEndTestTwo() {\n        final YearlyReminder reminder = new YearlyReminder();\n\n        final LocalDate startDate = LocalDate.ofYearDay(2012, 366);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(null);\n\n        assertEquals(reminder.getReminderType(), ReminderType.YEARLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.ofYearDay(2013, 365), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2014, 365), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2015, 365), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2016, 366), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2017, 365), iterator.next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2013, 365), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2014, 365), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2015, 365), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2016, 366), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2017, 365), reminder.getIterator().next());\n    }\n\n    @Test\n    void simpleTest() {\n        final YearlyReminder reminder = new YearlyReminder();\n\n        final LocalDate startDate = LocalDate.ofYearDay(2011, 364);\n\n        reminder.setIncrement(1);\n        reminder.setStartDate(startDate);\n        reminder.setEndDate(LocalDate.ofYearDay(2017, 365));\n\n        assertEquals(reminder.getReminderType(), ReminderType.YEARLY);\n\n        final RecurringIterator iterator = reminder.getIterator();\n\n        assertEquals(startDate, iterator.next());\n        assertEquals(LocalDate.of(2012, Month.DECEMBER, 29), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2013, 364), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2014, 364), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2015, 364), iterator.next());\n        assertEquals(LocalDate.of(2016, Month.DECEMBER, 29), iterator.next());\n        assertEquals(LocalDate.ofYearDay(2017, 364), iterator.next());\n        assertNull(iterator.next());\n\n        // leap year\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2012, Month.DECEMBER, 29), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2013, 364), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2014, 364), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2015, 364), reminder.getIterator().next());\n\n        // leap year\n        reminder.setLastDate();\n        assertEquals(LocalDate.of(2016, Month.DECEMBER, 29), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertEquals(LocalDate.ofYearDay(2017, 364), reminder.getIterator().next());\n\n        reminder.setLastDate();\n        assertNull(iterator.next());\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/report/poi/BudgetResultsExportTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.report.poi;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\nimport jgnash.engine.Account;\nimport jgnash.engine.AccountType;\nimport jgnash.engine.CurrencyNode;\nimport jgnash.engine.DataStoreType;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.engine.budget.Budget;\nimport jgnash.engine.budget.BudgetResultsModel;\nimport jgnash.time.Period;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * JUnit test class to export a {@code BudgetResultsModel}.\n *\n * @author Craig Cavanaugh\n */\nclass BudgetResultsExportTest {\n\n    @Test\n    void testExportBudgetResultsModel() throws Exception {\n\n        final String file = Files.createTempFile(\"budget-\",\n                DataStoreType.XML.getDataStore().getFileExt()).toString();\n\n        EngineFactory.deleteDatabase(file);\n\n        Engine e = EngineFactory.bootLocalEngine(file, EngineFactory.DEFAULT, EngineFactory.EMPTY_PASSWORD,\n                DataStoreType.XML);\n        e.setCreateBackups(false);\n\n        CurrencyNode node = e.getDefaultCurrency();\n\n        Account account1 = new Account(AccountType.EXPENSE, node);\n        account1.setName(\"Expense 1\");\n        e.addAccount(e.getRootAccount(), account1);\n\n        Account account2 = new Account(AccountType.EXPENSE, node);\n        account2.setName(\"Expense 2\");\n        e.addAccount(e.getRootAccount(), account2);\n\n        Budget budget = new Budget();\n        budget.setName(\"My Budget\");\n        budget.setDescription(\"Test\");\n        budget.setBudgetPeriod(Period.MONTHLY);\n\n        assertTrue(e.addBudget(budget));\n\n        BudgetResultsModel model = new BudgetResultsModel(budget, 2012, node, false);\n\n        final Path exportFile = Files.createTempFile(\"testworkbook\", \".xls\");\n\n        final String errors = BudgetResultsExport.exportBudgetResultsModel(exportFile, model);\n\n        assertNull(errors);\n\n        assertTrue(Files.exists(exportFile));\n\n        Files.delete(exportFile);\n\n        EngineFactory.closeEngine(EngineFactory.DEFAULT);\n\n        Files.deleteIfExists(Paths.get(file));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/text/NumericFormatsTests.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.text;\n\nimport jgnash.engine.CurrencyNode;\nimport org.junit.jupiter.api.Test;\n\nimport java.math.BigDecimal;\nimport java.text.NumberFormat;\nimport java.util.Locale;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass NumericFormatsTests {\n\n    @Test\n    void localeTest() {\n\n        Locale.setDefault(new Locale(\"el\", \"GR\"));\n\n        CurrencyNode currencyNode = new CurrencyNode();\n        currencyNode.setSymbol(\"EUR\");\n        currencyNode.setScale((byte) 2);\n        currencyNode.setPrefix(\"€\");\n\n        final NumberFormat numberFormat = NumericFormats.getFullCommodityFormat(currencyNode);\n\n        assertEquals(\"EUR\", numberFormat.getCurrency().getCurrencyCode());\n\n        assertThrows(NullPointerException.class, () -> NumericFormats.getFullCommodityFormat(null));\n    }\n\n    @Test void testPercentages() {\n        final NumberFormat format = NumericFormats.getPercentageFormat();\n\n        assertEquals(\"12.34%\", format.format(.12344));\n        assertEquals(\"12.35%\", format.format(.12345));\n    }\n\n    @Test void testFixedPrecisionFormat() {\n        assertEquals(\"12.34\", NumericFormats.getFixedPrecisionFormat(2).format(12.344));\n        assertEquals(\"12.35\", NumericFormats.getFixedPrecisionFormat(2).format(12.345));\n    }\n\n    @Test\n    void knowCurrencyFormats() {\n        System.out.println(\"Full formats\");\n        for (String pattern: NumericFormats.getKnownFullPatterns()) {\n            System.out.println(pattern);\n        }\n\n\n        System.out.println(\"Short formats\");\n        for (String pattern: NumericFormats.getKnownShortPatterns()) {\n            System.out.println(pattern);\n        }\n    }\n\n    @Test\n    void testFormats() {\n        Locale.setDefault(Locale.US);\n\n        final CurrencyNode node = new CurrencyNode();\n        node.setSymbol(\"USD\");\n        node.setScale((byte) 2);\n        node.setPrefix(\"$\");\n\n        // preserve the configured format\n        final String oldShortPattern = NumericFormats.getShortFormatPattern();\n        final String oldFullPattern = NumericFormats.getFullFormatPattern();\n\n        NumericFormats.setFullFormatPattern(\"\\u00A4#,##0.00;(\\u00A4#,##0.00)\");\n        NumericFormats.setShortFormatPattern(\"\\u00A4#,##0.00;-\\u00A4#,##0.00\");\n\n        NumberFormat shortFormat = NumericFormats.getShortCommodityFormat(node);\n        NumberFormat fullFormat = NumericFormats.getFullCommodityFormat(node);\n\n        assertNotNull(fullFormat);\n        assertNotNull(shortFormat);\n\n        assertEquals(\"$10.00\", shortFormat.format(BigDecimal.TEN));\n        assertEquals(\"$10.00 \", fullFormat.format(BigDecimal.TEN));\n\n        assertEquals(\"-$10.00\", shortFormat.format(BigDecimal.TEN.negate()));\n        assertEquals(\"($10.00)\", fullFormat.format(BigDecimal.TEN.negate()));\n\n        NumericFormats.setShortFormatPattern(\"#,##0.00;-#,##0.00\");\n        shortFormat = NumericFormats.getShortCommodityFormat(node);\n\n        assertEquals(\"10.00\", shortFormat.format(BigDecimal.TEN));\n        assertEquals(\"-10.00\", shortFormat.format(BigDecimal.TEN.negate()));\n\n        // restore the old patterns\n        NumericFormats.setShortFormatPattern(oldShortPattern);\n        NumericFormats.setFullFormatPattern(oldFullPattern);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/BinaryXStreamTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.OutputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\n\nimport com.thoughtworks.xstream.XStream;\nimport com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;\nimport com.thoughtworks.xstream.io.binary.BinaryStreamDriver;\n\nimport jgnash.engine.xstream.XStreamJVM9;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * Unit test for binary xstream.\n *\n * @author Craig Cavanaugh\n */\nclass BinaryXStreamTest {\n\n    private static Path tempFile;\n\n    @Test\n    void testFile() {\n\n        final List<String> stringData = new ArrayList<>();\n\n        for (int i = 0; i < 100; i++) {\n            stringData.add(UUID.randomUUID().toString());\n        }\n\n        final List<Integer> integerData = stringData.stream().map(String::hashCode).collect(Collectors.toList());\n\n        try {\n            tempFile = Files.createTempFile(\"test\", \"\");\n        } catch (IOException e) {\n            fail(e.toString());\n        }\n\n\n        try (final OutputStream fos = Files.newOutputStream(tempFile)) {\n            BinaryStreamDriver bsd = new BinaryStreamDriver();\n            XStream xstream = new XStreamJVM9(new PureJavaReflectionProvider(), bsd);\n\n            try (ObjectOutputStream out = xstream.createObjectOutputStream(fos)) {\n                out.writeObject(stringData);\n                out.writeObject(integerData);\n            }\n        } catch (IOException e) {\n            fail(e.toString());\n        }\n\n        assertTrue(FileMagic.isBinaryXStreamFile(tempFile));\n        assertFalse(FileMagic.isOfxV2(tempFile));\n\n        try (InputStream fis = Files.newInputStream(tempFile)) {\n            BinaryStreamDriver bsd = new BinaryStreamDriver();\n            XStream xstream = new XStreamJVM9(new PureJavaReflectionProvider(), bsd);\n\n            try (ObjectInputStream in = xstream.createObjectInputStream(fis)) {\n                List<?> strings = (List<?>) in.readObject();\n                List<?> integers = (List<?>) in.readObject();\n\n                assertArrayEquals(strings.toArray(), stringData.toArray());\n                assertArrayEquals(integers.toArray(), integerData.toArray());\n            } catch (ClassNotFoundException e) {\n                fail(e.toString());\n            }\n        } catch (IOException e) {\n            fail(e.toString());\n        }\n\n    }\n\n    @AfterAll\n    static void cleanup() throws IOException {\n        Files.deleteIfExists(tempFile);\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/DateFormatTest.java",
    "content": "package jgnash.util;\n\nimport java.util.Set;\n\nimport jgnash.time.DateUtils;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\n\nclass DateFormatTest {\n\n    @Test\n    void dateFormatTest() {\n        final Set<String> dateFormats = DateUtils.getAvailableShortDateFormats();\n\n        assertNotEquals(0, dateFormats.size());\n\n        dateFormats.forEach(System.out::println);\n    }\n}"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/DateTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\nimport java.time.Month;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Date;\n\nimport jgnash.time.DateUtils;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * Unit test for date utilities.\n *\n * @author Craig Cavanaugh\n */\nclass DateTest {\n\n    @Test\n    void formatTestOne() throws ParseException {\n        final SimpleDateFormat format = new SimpleDateFormat(\"yyMMdd\");\n        final Date date = format.parse(\"121206\");\n\n        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(\"yyMMdd\");\n        final LocalDate localDate = LocalDate.from(dateTimeFormatter.parse(\"121206\"));\n\n        assertEquals(localDate, DateUtils.asLocalDate(date));\n    }\n\n    @Test\n    void epochTest() {\n        final LocalDate now = LocalDate.now();\n\n        // round trip\n        assertEquals(now, DateUtils.asLocalDate(DateUtils.asEpochMilli(now)));\n    }\n\n    @Test\n    void getFirstDayWeeklyTest() {\n        // test days for year 2011\n        LocalDate[] days = DateUtils.getFirstDayWeekly(2011);\n\n        /*for (final LocalDate day : days) {\n            System.out.println(day.toString());\n        }*/\n\n        assertEquals(52, days.length);\n\n        assertEquals(3, days[0].getDayOfMonth());\n\n        assertEquals(26, days[51].getDayOfMonth());\n\n        // test days for year 2004\n        days = DateUtils.getFirstDayWeekly(2004);\n\n\n        /*for (final LocalDate day : days) {\n            System.out.println(day.toString());\n        }*/\n\n        assertEquals(53, days.length);\n\n        assertEquals(29, days[0].getDayOfMonth());\n\n        assertEquals(20, days[51].getDayOfMonth());\n\n        assertEquals(27, days[52].getDayOfMonth());\n\n        // test days for year 2015\n        days = DateUtils.getFirstDayWeekly(2015);\n\n        /*for (final LocalDate day : days) {\n            System.out.println(day.toString());\n        }*/\n\n        assertEquals(53, days.length);\n\n        assertEquals(29, days[0].getDayOfMonth());\n\n        assertEquals(5, days[1].getDayOfMonth());\n\n        assertEquals(21, days[51].getDayOfMonth());\n\n        assertEquals(28, days[52].getDayOfMonth());\n    }\n\n    @Test\n    void getFirstDayBiWeeklyTest() {\n        // test days for year 2011\n        LocalDate[] days = DateUtils.getFirstDayBiWeekly(2011);\n\n        assertEquals(26, days.length);\n\n        days = DateUtils.getFirstDayBiWeekly(2015);\n        assertEquals(27, days.length);\n\n        assertEquals(29, days[0].getDayOfMonth());\n\n        assertEquals(12, days[1].getDayOfMonth());\n\n        assertEquals(28, days[26].getDayOfMonth());\n    }\n\n    @Test\n    void getFirstDaysInMonthTest() {\n        LocalDate[] days = DateUtils.getFirstDayMonthly(2011);\n\n        assertEquals(1, days[0].getDayOfYear());\n        assertEquals(1 + 31, days[1].getDayOfYear());\n        assertEquals(1 + 31 + 28, days[2].getDayOfYear());\n        assertEquals(1 + 31 + 28 + 31, days[3].getDayOfYear());\n\n        assertEquals(365 - 31 - 30 + 1, days[10].getDayOfYear());\n        assertEquals(365 - 31 + 1, days[11].getDayOfYear());\n    }\n\n    @Test\n    void getFirstDaysInMonthTest2() {\n        LocalDate[] days = DateUtils.getFirstDayMonthly(Month.JANUARY, 2011, 12);\n\n        assertEquals(12, days.length);\n        assertEquals(1, days[0].getDayOfYear());\n        assertEquals(1 + 31, days[1].getDayOfYear());\n        assertEquals(1 + 31 + 28, days[2].getDayOfYear());\n        assertEquals(1 + 31 + 28 + 31, days[3].getDayOfYear());\n\n        assertEquals(365 - 31 - 30 + 1, days[10].getDayOfYear());\n        assertEquals(365 - 31 + 1, days[11].getDayOfYear());\n\n        // roll past 2011 test\n        days = DateUtils.getFirstDayMonthly(Month.FEBRUARY, 2011, 12);\n\n        assertEquals(12, days.length);\n        assertEquals(2011, days[0].getYear());\n        assertEquals(2012, days[11].getYear());\n        assertEquals(DayOfWeek.SUNDAY, days[11].getDayOfWeek());\n    }\n\n    @Test\n    void quaterTest() {\n        LocalDate[] days = DateUtils.getFirstDayQuarterly(2019);\n\n        assertEquals(days[0], LocalDate.of(2019, 1, 1));\n        assertEquals(days[1], LocalDate.of(2019, 4, 1));\n        assertEquals(days[2], LocalDate.of(2019, 7, 1));\n        assertEquals(days[3], LocalDate.of(2019, 10, 1));\n\n\n        days = DateUtils.getFirstDayQuarterly(Month.APRIL, 2019, 4);\n\n        assertEquals(days[0], LocalDate.of(2019, 4, 1));\n        assertEquals(days[1], LocalDate.of(2019, 7, 1));\n        assertEquals(days[2], LocalDate.of(2019, 10, 1));\n        assertEquals(days[3], LocalDate.of(2020, 1, 1));\n    }\n\n    @Test\n    void getAllDaysTest() {\n        LocalDate[] days = DateUtils.getAllDays(2011);\n        assertEquals(365, days.length);\n\n        assertEquals(LocalDate.ofYearDay(2011, 1), days[0]);\n        assertEquals(LocalDate.ofYearDay(2011, 365), days[364]);\n\n        assertEquals(366, DateUtils.getAllDays(2000).length);\n    }\n\n    @Test\n    void weekOfYear() {\n        assertEquals(53, DateUtils.getWeekOfTheYear(LocalDate.ofYearDay(2016, 3)));\n        assertEquals(1, DateUtils.getWeekOfTheYear(LocalDate.ofYearDay(2016, 4)));\n    }\n\n    @Test\n    void weeksPerYear() {\n        assertEquals(53, DateUtils.getNumberOfWeeksInYear(2009));\n        assertEquals(52, DateUtils.getNumberOfWeeksInYear(2014));\n        assertEquals(53, DateUtils.getNumberOfWeeksInYear(2015));\n        assertEquals(52, DateUtils.getNumberOfWeeksInYear(2016));\n        assertEquals(53, DateUtils.getNumberOfWeeksInYear(2020));\n        assertEquals(52, DateUtils.getNumberOfWeeksInYear(2025));\n        assertEquals(53, DateUtils.getNumberOfWeeksInYear(2026));\n        assertEquals(53, DateUtils.getNumberOfWeeksInYear(2032));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/EncodeDecodeTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\n/**\n * Unit test for Encoding and Decoding arrays of doubles.\n *\n * @author Craig Cavanaugh\n */\nclass EncodeDecodeTest {\n\n    @Test\n    void testEncodeDecodeDoubleArrays() {\n        final double[] base = new double[]{10.231, 11.35, 45.34, 2.0, 4.0, 9.0, 0};\n        final String result = EncodeDecode.encodeDoubleArray(base);\n\n        assertNotNull(result);\n        System.out.println(result);\n\n        assertEquals(\"10.23,11.35,45.34,2,4,9,0\", result);\n\n        final double[] returnResult = EncodeDecode.decodeDoubleArray(result);\n\n        assertArrayEquals(base, returnResult, .001);\n    }\n\n    @Test\n    void testEncodeStringCollection() {\n        List<String> items = Arrays.asList(\"apple\", \"pear\", \"peach\", \"grapes\");\n\n        assertEquals(\"apple,pear,peach,grapes\", EncodeDecode.encodeStringCollection(items));\n    }\n\n    @Test\n    void testHexDecode() {\n        assertEquals(\"0x00000000\", EncodeDecode.longToColorString(0));\n        assertEquals(0, EncodeDecode.colorStringToLong(\"0x00000000\"));\n\n        assertEquals(\"0x00FFFFFF\", EncodeDecode.longToColorString(16777215));\n        assertEquals(16777215, EncodeDecode.colorStringToLong(\"0x00FFFFFF\"));\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/EncryptionManagerTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport org.apache.commons.text.RandomStringGenerator;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n/**\n * EncryptionManager test.\n *\n * @author Craig Cavanaugh\n */\nclass EncryptionManagerTest {\n\n    private static final RandomStringGenerator generator = new RandomStringGenerator.Builder().build();\n\n    private static final String PASSWORD = generator.generate(20);\n\n    @Test\n    void test() {\n        final EncryptionManager encryptionManager = new EncryptionManager(PASSWORD.toCharArray());\n\n        for (int i = 1; i < 8192; i++) {\n            String testString = generator.generate(i);\n\n            final String encrypted = encryptionManager.encrypt(testString);\n\n            final String decrypted = encryptionManager.decrypt(encrypted);\n\n            assertEquals(testString, decrypted);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/FileMagicTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.Paths;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n/**\n * Test FileMagic.\n * \n * @author Craig Cavanaugh\n */\nclass FileMagicTest {\n\n    @Test\n    void testH2Magic() throws URISyntaxException {\n        URL url = FileMagicTest.class.getResource(\"/h2-test.h2.db\");\n\n        FileMagic.FileType type = FileMagic.magic(Paths.get(url.toURI()));\n\n        assertEquals(FileMagic.FileType.h2, type);\n    }\n\n    /**\n     * Test for Ofx version 1 file identification.\n     */\n    @Test\n    void OfxV1Test() {\n\n        URL url = FileMagicTest.class.getResource(\"/bank1.ofx\");\n\n        try {\n            assertTrue(FileMagic.isOfxV1(Paths.get(url.toURI())));\n        } catch (URISyntaxException ex) {\n            Logger.getLogger(FileMagicTest.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        url = FileMagicTest.class.getResource(\"/ofx_spec201_stmtrs_example.xml\");\n\n        try {\n            assertFalse(FileMagic.isOfxV1(Paths.get(url.toURI())));\n        } catch (URISyntaxException ex) {\n            Logger.getLogger(FileMagicTest.class.getName()).log(Level.SEVERE, null, ex);\n        }\n    }\n\n    /**\n     * Test for Ofx version 1 file encoding.\n     */\n    @Test\n    void OfxV1EncodingTest1() {\n\n        URL url = FileMagicTest.class.getResource(\"/bank1.ofx\");\n\n        try {\n            assertEquals(\"windows-1252\", FileMagic.getOfxV1Encoding(Paths.get(url.toURI())));\n        } catch (URISyntaxException ex) {\n            Logger.getLogger(FileMagicTest.class.getName()).log(Level.SEVERE, null, ex);\n        }\n    }\n\n    /**\n     * Test for Ofx version 1 file encoding.\n     */\n    @Test\n    void OfxV1EncodingTest2() {\n\n        URL url = FileMagicTest.class.getResource(\"/File_with_Accents.ofx\");\n\n        try {\n            assertEquals(\"ISO-8859-1\", FileMagic.getOfxV1Encoding(Paths.get(url.toURI())));\n        } catch (final URISyntaxException ex) {\n            Logger.getLogger(FileMagicTest.class.getName()).log(Level.SEVERE, null, ex);\n        }\n    }\n\n    /**\n     * Test for Ofx version 2 file identification.\n     */\n    @Test\n    void OfxV2Test() {\n\n        URL url = FileMagicTest.class.getResource(\"/ofx_spec201_stmtrs_example.xml\");\n\n        try {\n            assertTrue(FileMagic.isOfxV2(Paths.get(url.toURI())));\n        } catch (URISyntaxException ex) {\n            Logger.getLogger(FileMagicTest.class.getName()).log(Level.SEVERE, null, ex);\n        }\n\n        url = FileMagicTest.class.getResource(\"/bank1.ofx\");\n\n        try {\n            assertFalse(FileMagic.isOfxV2(Paths.get(url.toURI())));\n        } catch (URISyntaxException ex) {\n            Logger.getLogger(FileMagicTest.class.getName()).log(Level.SEVERE, null, ex);\n        }\n    }\n}\n"
  },
  {
    "path": "jgnash-tests/src/test/java/jgnash/util/FileUtilsTest.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage jgnash.util;\n\nimport org.awaitility.Awaitility;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\n/**\n * File utilities test.\n *\n * @author Craig Cavanaugh\n */\nclass FileUtilsTest {\n\n    private static void checkTestData(final String testdata, final String absolutepath) throws IOException {\n        final char[] buffer = new char[testdata.length()];\n\n        try (final Reader reader = Files.newBufferedReader(Paths.get(absolutepath))) {\n            final int read = reader.read(buffer);\n\n            assertEquals(testdata.length(), read);\n        }\n\n        assertEquals(testdata, new String(buffer));\n    }\n\n    private static void writeTestData(final String testdata, final Path tempfile) throws IOException {\n        try (final Writer os = Files.newBufferedWriter(tempfile)) {\n            os.write(testdata);\n        }\n    }\n\n    @Test\n    void testFileLock() throws IOException {\n        final Path tempFile = Files.createTempFile(\"temp\", null);\n\n        try (final Writer writer = Files.newBufferedWriter(tempFile)) {\n            writer.write(\"test\");\n        } catch (Exception e) {\n            fail();\n        }\n\n        assertTrue(Files.isWritable(tempFile));\n        assertTrue(Files.isReadable(tempFile));\n\n        final FileLocker filelocker = new FileLocker();\n        filelocker.acquireLock(tempFile);\n\n        assertTrue(FileUtils.isFileLocked(tempFile.toString()));\n\n        filelocker.release();\n\n        assertFalse(FileUtils.isFileLocked(tempFile.toString()));\n\n        assertTrue(Files.deleteIfExists(tempFile));\n    }\n\n    @Test\n    void testWaitForFileRemoval() throws IOException {\n        final Path tempFile = Files.createTempFile(\"temp\", null);\n\n        try (final Writer writer = Files.newBufferedWriter(tempFile)) {\n            writer.write(\"test\");\n        } catch (Exception e) {\n            fail();\n        }\n\n        assertTrue(Files.isWritable(tempFile));\n        assertTrue(Files.isReadable(tempFile));\n\n        new Thread(() -> {\n            try {\n                Thread.sleep(500);\n                Files.delete(tempFile);\n            } catch (final InterruptedException | IOException e) {\n                e.printStackTrace();\n                fail();\n            }\n        }).start();\n\n        assertTrue(FileUtils.waitForFileRemoval(tempFile.toString(), 1000));\n\n        Path tempFile2 = Files.createTempFile(\"temp\", null);\n\n        try (final Writer writer = Files.newBufferedWriter(tempFile)) {\n            writer.write(\"test\");\n        } catch (Exception e) {\n            fail();\n        }\n\n        assertTrue(Files.isWritable(tempFile2));\n        assertTrue(Files.isReadable(tempFile2));\n\n        AtomicBoolean lock = new AtomicBoolean(false);\n\n        new Thread(() -> {\n            try {\n                Thread.sleep(1500);\n                Files.delete(tempFile2);\n                lock.set(true);\n            } catch (final InterruptedException | IOException e) {\n                e.printStackTrace();\n                fail();\n            }\n        }).start();\n\n        assertFalse(FileUtils.waitForFileRemoval(tempFile2.toString(), 750));\n\n        Awaitility.waitAtMost(2000, TimeUnit.MILLISECONDS).untilTrue(lock);\n    }\n\n    @Test\n    void fileExtensionStripTest() {\n        assertTrue(FileUtils.fileHasExtension(\"text.txt\"));\n        assertTrue(FileUtils.fileHasExtension(\"text.txt.txt\"));\n        assertFalse(FileUtils.fileHasExtension(\"test\"));\n    }\n\n    @Test\n    void strip() {\n        assertEquals(\"database\", FileUtils.stripFileExtension(\"database.h2.db\"));\n        assertEquals(\"database\", FileUtils.stripFileExtension(\"database.xml\"));\n\n        assertEquals(\"database\", FileUtils.stripFileExtension(\"database.xyz\"));\n\n        assertEquals(\"/home/craig/test.test@xyz.com/jgnash files/craig\",\n                FileUtils.stripFileExtension(\"/home/craig/test.test@xyz.com/jgnash files/craig.xml\"));\n\n        assertEquals(\"/home/craig/test.test@xyz.com/jgnash files/craig\",\n                FileUtils.stripFileExtension(\"/home/craig/test.test@xyz.com/jgnash files/craig.h2.db\"));\n    }\n\n    @Test\n    void fileExtensionText() {\n        assertEquals(\"txt\", FileUtils.getFileExtension(\"test.txt\"));\n\n        assertEquals(\"h2.db\",\n                FileUtils.getFileExtension(\"/home/craig/test.test@xyz.com/jgnash files/craig.h2.db\"));\n\n        assertEquals(\"xml\", FileUtils.getFileExtension(\"database.xml\"));\n    }\n\n    @Test\n    void fileCopyToSelf() throws IOException {\n        Path tempFile = Files.createTempFile(\"jgnash-test\", \".jdb\");\n\n        String absolutePath = tempFile.toString();\n        String testData = \"42fd;lgkjsgj;gfj;slfkgj;\";\n\n        // Write the data to a file\n        writeTestData(testData, tempFile);\n        checkTestData(testData, absolutePath);\n\n        // Copy the file to itself: the file should not be emptied :)\n        assertFalse(FileUtils.copyFile(Paths.get(absolutePath), Paths.get(absolutePath)));\n\n        Files.delete(tempFile);\n    }\n}"
  },
  {
    "path": "jgnash-tests/src/test/resources/401k-header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<OFX>   <!-- Example Investment Account taken from the 2.2 Public Draft 3 OFX specification -->\n    <SIGNONMSGSRSV1>\n        <SONRS>                                                         <!-- Begin signon -->\n            <STATUS>                                                    <!-- Begin status aggregate -->\n                <CODE>0</CODE>                                          <!-- OK -->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <DTSERVER>20060131172532</DTSERVER>\n            <LANGUAGE>ENG</LANGUAGE>                                    <!-- Language used in response -->\n            <DTPROFUP>20060131172532</DTPROFUP>                         <!-- Last update to profile-->\n            <DTACCTUP>20060131172532</DTACCTUP>                         <!-- Last account update -->\n            <FI>                                                        <!-- ID of receiving institution -->\n                <ORG>NCH</ORG>                                          <!-- Name of ID owner -->\n                <FID>1001</FID>                                         <!-- Actual ID -->\n            </FI>\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <INVSTMTMSGSRSV1>\n        <INVSTMTTRNRS>                                                  <!--First request in file-->\n            <TRNUID>1002</TRNUID>                                       <!--Client ID for this request-->\n            <STATUS>\n                <CODE>0</CODE>                                           <!--0 = accepted, good data follows-->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <INVSTMTRS>                                                 <!--Beginning of statement download-->\n                <DTASOF>20060131172605.000[-4:EST]</DTASOF>            <!--Statement as of Jan 31, 2006 5:26pm-->\n                <CURDEF>USD</CURDEF>                                    <!--Default currency is US Dollar-->\n                <INVACCTFROM>                                           <!--Beginning of account information-->\n                    <BROKERID>121099999</BROKERID>                      <!--FI ID-->\n                    <ACCTID>999988</ACCTID>                             <!--Account number-->\n                </INVACCTFROM>                                          <!--End of account information-->\n                <INVTRANLIST>\n                    <DTSTART>20060105172532.000[-5:EST]</DTSTART>\n                    <DTEND>20060131172532.000[-4:EST]</DTEND>\n                    <BUYMF>\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>212839062820295310723</FITID>\n                                <DTTRADE>20060119000000.000[-5:EST]</DTTRADE>\n                            </INVTRAN>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>14.6860</UNITS>\n                            <UNITPRICE>18.9000</UNITPRICE>\n                            <TOTAL>-277.5700</TOTAL>\n                            <CURRENCY>\n                                <CURRATE>1.0000</CURRATE>\n                                <CURSYM>USD</CURSYM>\n                            </CURRENCY>\n                            <SUBACCTSEC>OTHER</SUBACCTSEC>\n                            <SUBACCTFUND>OTHER</SUBACCTFUND>\n                            <LOANID>2</LOANID>\n                            <LOANPRINCIPAL>277.5700</LOANPRINCIPAL>\n                            <LOANINTEREST>0.0000</LOANINTEREST>\n                            <INV401KSOURCE>ROLLOVER</INV401KSOURCE>\n                            <DTPAYROLL>20060114000000.000[-5:EST]</DTPAYROLL>\n                            <PRIORYEARCONTRIB>N</PRIORYEARCONTRIB>\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>\n                    </BUYMF>\n                    <BUYMF>\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>212839062820510822977</FITID>\n                                <DTTRADE>20060119000000.000[-5:EST]</DTTRADE>\n                            </INVTRAN>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>2.0220</UNITS>\n                            <UNITPRICE>18.9000</UNITPRICE>\n                            <TOTAL>-38.2200</TOTAL>\n                            <CURRENCY>\n                                <CURRATE>1.0000</CURRATE>\n                                <CURSYM>USD</CURSYM>\n                            </CURRENCY>\n                            <SUBACCTSEC>OTHER</SUBACCTSEC>\n                            <SUBACCTFUND>OTHER</SUBACCTFUND>\n                            <LOANID>2</LOANID>\n                            <LOANPRINCIPAL>0.0000</LOANPRINCIPAL>\n                            <LOANINTEREST>38.2200</LOANINTEREST>\n                            <INV401KSOURCE>ROLLOVER</INV401KSOURCE>\n                            <DTPAYROLL>20060114000000.000[-5:EST]</DTPAYROLL>\n                            <PRIORYEARCONTRIB>N</PRIORYEARCONTRIB>\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>\n                    </BUYMF>\n                    <BUYMF>\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>212849815151950488609</FITID>\n                                <DTTRADE>20060106000000.000[-5:EST]</DTTRADE>\n                            </INVTRAN>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>4.9010</UNITS>\n                            <UNITPRICE>18.7900</UNITPRICE>\n                            <TOTAL>-92.0900</TOTAL>\n                            <CURRENCY>\n                                <CURRATE>1.0000</CURRATE>\n                                <CURSYM>USD</CURSYM>\n                            </CURRENCY>\n                            <SUBACCTSEC>OTHER</SUBACCTSEC>\n                            <SUBACCTFUND>OTHER</SUBACCTFUND>\n                            <INV401KSOURCE>PRETAX</INV401KSOURCE>\n                            <DTPAYROLL>20051231000000.000[-5:EST]</DTPAYROLL>\n                            <PRIORYEARCONTRIB>Y</PRIORYEARCONTRIB>\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>\n                    </BUYMF>\n                </INVTRANLIST>\n                <INV401KBAL>\n                    <PRETAX>31690.340000</PRETAX>\n                    <PROFITSHARING>10725.640000</PROFITSHARING>\n                    <ROLLOVER>15945.750000</ROLLOVER>\n                    <OTHERVEST>108.800000</OTHERVEST>\n                    <TOTAL>58470.530000</TOTAL>\n                </INV401KBAL>\n                <INV401K>\n                    <EMPLOYERNAME>ELGIN NATIONAL INDUSTRIES INC</EMPLOYERNAME>\n                    <PLANID>4343</PLANID>\n                    <PLANJOINDATE>19940101000000.000[-5:EST]</PLANJOINDATE>\n                    <MATCHINFO>\n                        <MATCHPCT>0.00</MATCHPCT>\n                    </MATCHINFO>\n                    <CONTRIBINFO>\n                        <CONTRIBSECURITY>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <PRETAXCONTRIBPCT>50.0000</PRETAXCONTRIBPCT>\n                            <PROFITSHARINGCONTRIBPCT>100.0000\n                            </PROFITSHARINGCONTRIBPCT>\n                            <ROLLOVERCONTRIBPCT>100.0000</ROLLOVERCONTRIBPCT>\n                            <OTHERVESTPCT>100.0000</OTHERVESTPCT>\n                        </CONTRIBSECURITY>\n                        <CONTRIBSECURITY>\n                            <SECID>\n                                <UNIQUEID>74431M105</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <PRETAXCONTRIBPCT>25.0000</PRETAXCONTRIBPCT>\n                            <PROFITSHARINGCONTRIBPCT>0.0000\n                            </PROFITSHARINGCONTRIBPCT>\n                            <ROLLOVERCONTRIBPCT>0.0000</ROLLOVERCONTRIBPCT>\n                            <OTHERVESTPCT>0.0000</OTHERVESTPCT>\n                        </CONTRIBSECURITY>\n                        <CONTRIBSECURITY>\n                            <SECID>\n                                <UNIQUEID>743969107</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <PRETAXCONTRIBPCT>25.0000</PRETAXCONTRIBPCT>\n                            <PROFITSHARINGCONTRIBPCT>0.0000\n                            </PROFITSHARINGCONTRIBPCT>\n                            <ROLLOVERCONTRIBPCT>0.0000</ROLLOVERCONTRIBPCT>\n                            <OTHERVESTPCT>0.0000</OTHERVESTPCT>\n                        </CONTRIBSECURITY>\n                    </CONTRIBINFO>\n                    <INV401KSUMMARY>\n                        <YEARTODATE>\n                            <DTSTART>20060101000000</DTSTART>\n                            <DTEND>20060131000000</DTEND>\n                            <CONTRIBUTIONS>\n                                <PRETAX>843.2500</PRETAX>\n                                <AFTERTAX>43.4200</AFTERTAX>\n                                <MATCH>421.6200</MATCH>\n                                <TOTAL>1308.2900</TOTAL>\n                            </CONTRIBUTIONS>\n                        </YEARTODATE>\n                    </INV401KSUMMARY>\n                </INV401K>\n            </INVSTMTRS>\n        </INVSTMTTRNRS>\n    </INVSTMTMSGSRSV1>\n    <SECLISTMSGSRSV1>\n        <SECLIST>                                                       <!--Beginning of securities list-->\n            <STOCKINFO>                                                 <!--Beginning of 1st security ID-->\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>123456789</UNIQUEID>                  <!--CUSIP for the stock -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>Acme Development, Inc.</SECNAME>\n                    <TICKER>ACME</TICKER>                               <!--Ticker symbol-->\n                    <FIID>1024</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <YIELD>10</YIELD>                                       <!--10% yield-->\n                <ASSETCLASS>SMALLSTOCK</ASSETCLASS>                     <!--Small Capital Stock asset class-->\n            </STOCKINFO>                                                <!--End of security ID-->\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>666678578</UNIQUEID>                  <!--CUSIP for the stock -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>Hackson Unlimited, Inc.</SECNAME>\n                    <TICKER>HACK</TICKER>                               <!--Ticker symbol-->\n                    <FIID>1027</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <YIELD>17</YIELD>                                       <!--17% yield-->\n                <ASSETCLASS>SMALLSTOCK</ASSETCLASS>                     <!--Small Capital Stock asset class-->\n            </STOCKINFO>\n            <OPTINFO>\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>000342222</UNIQUEID>                  <!--CUSIP for the option -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>                                            <!--End of security ID-->\n                    <SECNAME>Lucky Airlines Jan 97 Put</SECNAME>\n                    <TICKER>LUAXX</TICKER>                              <!--Ticker symbol-->\n                    <FIID>0013</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <OPTTYPE>PUT</OPTTYPE>\n                <STRIKEPRICE>35.00</STRIKEPRICE>                        <!--Strike price $35/share-->\n                <DTEXPIRE>20050121</DTEXPIRE>                           <!--Option expires Jan 21, 2005-->\n                <SHPERCTRCT>100</SHPERCTRCT>                            <!--100 shares per contract-->\n                <SECID>                                                 <!--Security ID-->\n                    <UNIQUEID>000342200</UNIQUEID>                      <!--CUSIP for the underlying stock -->\n                    <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                </SECID>\n                <ASSETCLASS>LARGESTOCK</ASSETCLASS>                     <!--Large Capital Stock asset class-->\n            </OPTINFO>                                                  <!--End of option information-->\n        </SECLIST>                                                      <!--End of securities list-->\n    </SECLISTMSGSRSV1>\n</OFX>"
  },
  {
    "path": "jgnash-tests/src/test/resources/401k.xml",
    "content": "<OFX>   <!-- Example Investment Account taken from the 2.2 Public Draft 3 OFX specification -->\n    <SIGNONMSGSRSV1>\n        <SONRS>                                                         <!-- Begin signon -->\n            <STATUS>                                                    <!-- Begin status aggregate -->\n                <CODE>0</CODE>                                          <!-- OK -->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <DTSERVER>20060131172532</DTSERVER>\n            <LANGUAGE>ENG</LANGUAGE>                                    <!-- Language used in response -->\n            <DTPROFUP>20060131172532</DTPROFUP>                         <!-- Last update to profile-->\n            <DTACCTUP>20060131172532</DTACCTUP>                         <!-- Last account update -->\n            <FI>                                                        <!-- ID of receiving institution -->\n                <ORG>NCH</ORG>                                          <!-- Name of ID owner -->\n                <FID>1001</FID>                                         <!-- Actual ID -->\n            </FI>\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <INVSTMTMSGSRSV1>\n        <INVSTMTTRNRS>                                                  <!--First request in file-->\n            <TRNUID>1002</TRNUID>                                       <!--Client ID for this request-->\n            <STATUS>\n                <CODE>0</CODE>                                           <!--0 = accepted, good data follows-->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <INVSTMTRS>                                                 <!--Beginning of statement download-->\n                <DTASOF>20060131172605.000[-4:EST]</DTASOF>            <!--Statement as of Jan 31, 2006 5:26pm-->\n                <CURDEF>USD</CURDEF>                                    <!--Default currency is US Dollar-->\n                <INVACCTFROM>                                           <!--Beginning of account information-->\n                    <BROKERID>121099999</BROKERID>                      <!--FI ID-->\n                    <ACCTID>999988</ACCTID>                             <!--Account number-->\n                </INVACCTFROM>                                          <!--End of account information-->\n                <INVTRANLIST>\n                    <DTSTART>20060105172532.000[-5:EST]</DTSTART>\n                    <DTEND>20060131172532.000[-4:EST]</DTEND>\n                    <BUYMF>\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>212839062820295310723</FITID>\n                                <DTTRADE>20060119000000.000[-5:EST]</DTTRADE>\n                            </INVTRAN>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>14.6860</UNITS>\n                            <UNITPRICE>18.9000</UNITPRICE>\n                            <TOTAL>-277.5700</TOTAL>\n                            <CURRENCY>\n                                <CURRATE>1.0000</CURRATE>\n                                <CURSYM>USD</CURSYM>\n                            </CURRENCY>\n                            <SUBACCTSEC>OTHER</SUBACCTSEC>\n                            <SUBACCTFUND>OTHER</SUBACCTFUND>\n                            <LOANID>2</LOANID>\n                            <LOANPRINCIPAL>277.5700</LOANPRINCIPAL>\n                            <LOANINTEREST>0.0000</LOANINTEREST>\n                            <INV401KSOURCE>ROLLOVER</INV401KSOURCE>\n                            <DTPAYROLL>20060114000000.000[-5:EST]</DTPAYROLL>\n                            <PRIORYEARCONTRIB>N</PRIORYEARCONTRIB>\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>\n                    </BUYMF>\n                    <BUYMF>\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>212839062820510822977</FITID>\n                                <DTTRADE>20060119000000.000[-5:EST]</DTTRADE>\n                            </INVTRAN>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>2.0220</UNITS>\n                            <UNITPRICE>18.9000</UNITPRICE>\n                            <TOTAL>-38.2200</TOTAL>\n                            <CURRENCY>\n                                <CURRATE>1.0000</CURRATE>\n                                <CURSYM>USD</CURSYM>\n                            </CURRENCY>\n                            <SUBACCTSEC>OTHER</SUBACCTSEC>\n                            <SUBACCTFUND>OTHER</SUBACCTFUND>\n                            <LOANID>2</LOANID>\n                            <LOANPRINCIPAL>0.0000</LOANPRINCIPAL>\n                            <LOANINTEREST>38.2200</LOANINTEREST>\n                            <INV401KSOURCE>ROLLOVER</INV401KSOURCE>\n                            <DTPAYROLL>20060114000000.000[-5:EST]</DTPAYROLL>\n                            <PRIORYEARCONTRIB>N</PRIORYEARCONTRIB>\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>\n                    </BUYMF>\n                    <BUYMF>\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>212849815151950488609</FITID>\n                                <DTTRADE>20060106000000.000[-5:EST]</DTTRADE>\n                            </INVTRAN>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>4.9010</UNITS>\n                            <UNITPRICE>18.7900</UNITPRICE>\n                            <TOTAL>-92.0900</TOTAL>\n                            <CURRENCY>\n                                <CURRATE>1.0000</CURRATE>\n                                <CURSYM>USD</CURSYM>\n                            </CURRENCY>\n                            <SUBACCTSEC>OTHER</SUBACCTSEC>\n                            <SUBACCTFUND>OTHER</SUBACCTFUND>\n                            <INV401KSOURCE>PRETAX</INV401KSOURCE>\n                            <DTPAYROLL>20051231000000.000[-5:EST]</DTPAYROLL>\n                            <PRIORYEARCONTRIB>Y</PRIORYEARCONTRIB>\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>\n                    </BUYMF>\n                </INVTRANLIST>\n                <INV401KBAL>\n                    <PRETAX>31690.340000</PRETAX>\n                    <PROFITSHARING>10725.640000</PROFITSHARING>\n                    <ROLLOVER>15945.750000</ROLLOVER>\n                    <OTHERVEST>108.800000</OTHERVEST>\n                    <TOTAL>58470.530000</TOTAL>\n                </INV401KBAL>\n                <INV401K>\n                    <EMPLOYERNAME>ELGIN NATIONAL INDUSTRIES INC</EMPLOYERNAME>\n                    <PLANID>4343</PLANID>\n                    <PLANJOINDATE>19940101000000.000[-5:EST]</PLANJOINDATE>\n                    <MATCHINFO>\n                        <MATCHPCT>0.00</MATCHPCT>\n                    </MATCHINFO>\n                    <CONTRIBINFO>\n                        <CONTRIBSECURITY>\n                            <SECID>\n                                <UNIQUEID>744316100</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <PRETAXCONTRIBPCT>50.0000</PRETAXCONTRIBPCT>\n                            <PROFITSHARINGCONTRIBPCT>100.0000\n                            </PROFITSHARINGCONTRIBPCT>\n                            <ROLLOVERCONTRIBPCT>100.0000</ROLLOVERCONTRIBPCT>\n                            <OTHERVESTPCT>100.0000</OTHERVESTPCT>\n                        </CONTRIBSECURITY>\n                        <CONTRIBSECURITY>\n                            <SECID>\n                                <UNIQUEID>74431M105</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <PRETAXCONTRIBPCT>25.0000</PRETAXCONTRIBPCT>\n                            <PROFITSHARINGCONTRIBPCT>0.0000\n                            </PROFITSHARINGCONTRIBPCT>\n                            <ROLLOVERCONTRIBPCT>0.0000</ROLLOVERCONTRIBPCT>\n                            <OTHERVESTPCT>0.0000</OTHERVESTPCT>\n                        </CONTRIBSECURITY>\n                        <CONTRIBSECURITY>\n                            <SECID>\n                                <UNIQUEID>743969107</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <PRETAXCONTRIBPCT>25.0000</PRETAXCONTRIBPCT>\n                            <PROFITSHARINGCONTRIBPCT>0.0000\n                            </PROFITSHARINGCONTRIBPCT>\n                            <ROLLOVERCONTRIBPCT>0.0000</ROLLOVERCONTRIBPCT>\n                            <OTHERVESTPCT>0.0000</OTHERVESTPCT>\n                        </CONTRIBSECURITY>\n                    </CONTRIBINFO>\n                    <INV401KSUMMARY>\n                        <YEARTODATE>\n                            <DTSTART>20060101000000</DTSTART>\n                            <DTEND>20060131000000</DTEND>\n                            <CONTRIBUTIONS>\n                                <PRETAX>843.2500</PRETAX>\n                                <AFTERTAX>43.4200</AFTERTAX>\n                                <MATCH>421.6200</MATCH>\n                                <TOTAL>1308.2900</TOTAL>\n                            </CONTRIBUTIONS>\n                        </YEARTODATE>\n                    </INV401KSUMMARY>\n                </INV401K>\n            </INVSTMTRS>\n        </INVSTMTTRNRS>\n    </INVSTMTMSGSRSV1>\n    <SECLISTMSGSRSV1>\n        <SECLIST>                                                       <!--Beginning of securities list-->\n            <STOCKINFO>                                                 <!--Beginning of 1st security ID-->\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>123456789</UNIQUEID>                  <!--CUSIP for the stock -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>Acme Development, Inc.</SECNAME>\n                    <TICKER>ACME</TICKER>                               <!--Ticker symbol-->\n                    <FIID>1024</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <YIELD>10</YIELD>                                       <!--10% yield-->\n                <ASSETCLASS>SMALLSTOCK</ASSETCLASS>                     <!--Small Capital Stock asset class-->\n            </STOCKINFO>                                                <!--End of security ID-->\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>666678578</UNIQUEID>                  <!--CUSIP for the stock -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>Hackson Unlimited, Inc.</SECNAME>\n                    <TICKER>HACK</TICKER>                               <!--Ticker symbol-->\n                    <FIID>1027</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <YIELD>17</YIELD>                                       <!--17% yield-->\n                <ASSETCLASS>SMALLSTOCK</ASSETCLASS>                     <!--Small Capital Stock asset class-->\n            </STOCKINFO>\n            <OPTINFO>\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>000342222</UNIQUEID>                  <!--CUSIP for the option -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>                                            <!--End of security ID-->\n                    <SECNAME>Lucky Airlines Jan 97 Put</SECNAME>\n                    <TICKER>LUAXX</TICKER>                              <!--Ticker symbol-->\n                    <FIID>0013</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <OPTTYPE>PUT</OPTTYPE>\n                <STRIKEPRICE>35.00</STRIKEPRICE>                        <!--Strike price $35/share-->\n                <DTEXPIRE>20050121</DTEXPIRE>                           <!--Option expires Jan 21, 2005-->\n                <SHPERCTRCT>100</SHPERCTRCT>                            <!--100 shares per contract-->\n                <SECID>                                                 <!--Security ID-->\n                    <UNIQUEID>000342200</UNIQUEID>                      <!--CUSIP for the underlying stock -->\n                    <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                </SECID>\n                <ASSETCLASS>LARGESTOCK</ASSETCLASS>                     <!--Large Capital Stock asset class-->\n            </OPTINFO>                                                  <!--End of option information-->\n        </SECLIST>                                                      <!--End of securities list-->\n    </SECLISTMSGSRSV1>\n</OFX>"
  },
  {
    "path": "jgnash-tests/src/test/resources/File_with_Accents.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:TYPE1\nENCODING:USASCII\nCHARSET:8859-1\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n<MESSAGE>OK\n</STATUS>\n<DTSERVER>20100101120000\n<USERKEY>001\n<INTU.BID>00012\n<LANGUAGE>FRA\n</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>ABCD-2010010112000012064\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n<MESSAGE>OK\n</STATUS>\n<STMTRS>\n<CURDEF>CAD\n<BANKACCTFROM>\n<BANKID>111111111\n<BRANCHID>2222222\n<ACCTID>123-45678-9012345\n<ACCTTYPE>CHECKING\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20091201120000\n<DTEND>20091230120000\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20091201120000\n<TRNAMT>-30.00\n<FITID>asdf23f\n<NAME>First Transaction\n<MEMO>RA\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20091202120000\n<TRNAMT>-45.00\n<FITID>asdf23f\n<NAME>Second Transaction ()\n<MEMO>RA\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20091203120000\n<TRNAMT>-60.00\n<FITID>sdf34RT4\n<NAME>Third Transaction\n<MEMO>RA\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>1000.00\n<DTASOF>20100101120000\n</LEDGERBAL>\n<AVAILBAL>\n<BALAMT>1000.00\n<DTASOF>20100101120000\n</AVAILBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>\n\n\n \t  \t \n"
  },
  {
    "path": "jgnash-tests/src/test/resources/IEX-IBM-1y.csv",
    "content": "date,open,close,high,low,volume,uOpen,uClose,uHigh,uLow,uVolume,change,changePercent,label,changeOverTime\n2018-11-09,128.46,128.97,126.97,125.85,7079854,125.02,126,130.24,125.6,7085982,0,0,\"Nov 9, 18\",0\n2018-11-12,127.4,121,126.09,122.64,5504627,124.5,126.3,124.42,125.03,5430807,-2.72,-2.239,\"Nov 12, 18\",-0.02151\n2018-11-13,123.85,124.96,124.05,120.6,4112431,122.72,125.67,123.66,122.6,4281492,-0.06,-0.0515,\"Nov 13, 18\",-0.021918\n2018-11-14,123.79,121.1,125.31,120.99,4888230,123.34,124,127.57,123.16,4826194,-0.64,-0.5558,\"Nov 14, 18\",-0.027957\n2018-11-15,124.78,125.16,126.18,121.51,5464312,122.35,126.9,122.07,123.01,5233130,1.29,1.07,\"Nov 15, 18\",-0.017693\n2018-11-16,121.82,125.13,126.17,121.79,4148406,126.45,124.42,125.22,123.25,4110607,0.14,0.108,\"Nov 16, 18\",-0.016164\n2018-11-19,126.38,125.92,123.26,125.42,4316212,121.94,126.12,124.5,123.72,4218359,-1.31,-1.0635,\"Nov 19, 18\",-0.02683\n2018-11-20,120.59,120,124.73,121.27,6516714,119.55,120.5,124.1,120.99,6262211,-3.15,-2.654,\"Nov 20, 18\",-0.053263\n2018-11-21,117.83,120.81,121.9,120.34,5361550,120.38,121.19,122.45,122.94,5512060,1.44,1.1867,\"Nov 21, 18\",-0.04083\n2018-11-23,121.26,117.95,118.99,122.09,2398875,119.3,118.39,121.53,121.58,2394043,-1.39,-1.1995,\"Nov 23, 18\",-0.0532\n2018-11-26,120.49,125.47,122.44,120.95,5028888,122.9,122.87,125.08,118.62,5220803,2.44,2.0401,\"Nov 26, 18\",-0.033664\n2018-11-27,121.84,123.04,124.94,119.18,5103745,122.45,123.68,121.41,121.74,5104689,0.47,0.4102,\"Nov 27, 18\",-0.029639\n2018-11-28,123.64,128,124.04,125.27,5265649,121.76,125,125.26,124.03,5201986,3.1,2.5414,\"Nov 28, 18\",-0.004464\n2018-11-29,123.75,121.85,125.52,126.2,5403398,127.29,121.81,124.42,123,5207654,-1.52,-1.2745,\"Nov 29, 18\",-0.016926\n2018-11-30,124.65,126.38,130.34,125.73,7469594,127.51,127.36,129.17,124.2,7559371,2.82,2.3519,\"Nov 30, 18\",0.005983\n2018-12-03,127.34,127.48,129.55,124.89,5934418,131.38,125.66,130.48,128.08,6058215,1.06,0.8421,\"Dec 3, 18\",0.01494\n2018-12-04,130.67,124.4,125.05,123.75,6173245,128.71,126.4,125.21,125.69,6014803,-3.88,-3.0995,\"Dec 4, 18\",-0.016476\n2018-12-06,123.7,126.45,127.94,123.2,7188880,120.75,129.37,124.83,121.8,7209830,2.32,1.928,\"Dec 6, 18\",0.003109\n2018-12-07,124.2,121.73,126.49,120.72,7205961,126.5,121.96,124.98,121.81,7147001,-4.78,-3.7457,\"Dec 7, 18\",-0.03407\n2018-12-10,123.83,121.95,127.63,118.91,6681525,123.46,125.29,122.48,119.15,6698293,1.87,1.57,\"Dec 10, 18\",-0.020239\n2018-12-11,124.82,123.71,124.25,122,5187086,127.61,122.27,127.77,123,5064890,-0.24,-0.2076,\"Dec 11, 18\",-0.022489\n2018-12-12,125.24,125.32,124.61,125,3701925,123.9,126.15,122.95,122,3674590,0.27,0.2265,\"Dec 12, 18\",-0.020135\n2018-12-13,125.31,126.69,125.08,125.18,4165728,127.3,125.97,123.77,123.88,4139951,-0.43,-0.3684,\"Dec 13, 18\",-0.023429\n2018-12-14,123.9,122.4,121.17,122.35,4768134,123.5,124.9,121.91,121.05,4764472,-0.85,-0.7153,\"Dec 14, 18\",-0.030277\n2018-12-17,124.73,118,124,118.4,7438513,120.32,116.3,121.15,115.74,7481545,-4,-3.248,\"Dec 17, 18\",-0.061903\n2018-12-18,118,121.84,118.38,119.09,6507911,117.8,117.75,123.59,117.07,6368181,0.56,0.4848,\"Dec 18, 18\",-0.057394\n2018-12-19,120.64,116.86,120.31,121.68,7328332,119.43,121.78,123.07,115.98,7282701,-0.23,-0.196,\"Dec 19, 18\",-0.057614\n2018-12-20,121.2,117.26,121.27,114.4,8768469,115.7,118.13,120.41,117.2,8868049,-3.42,-3.0529,\"Dec 20, 18\",-0.087397\n2018-12-21,118.1,114.39,117.29,112.68,10799500,116.4,114.88,115.89,114.12,10626407,-2.12,-1.8499,\"Dec 21, 18\",-0.10379\n2018-12-24,115,111.46,116,111,4002457,114.9,111.35,113,108.2,3888408,-3.53,-3.0866,\"Dec 24, 18\",-0.13231\n2018-12-26,113,111.46,115.05,107.93,6873075,110,111.7,115.28,107.19,6821613,3.83,3.5736,\"Dec 26, 18\",-0.098786\n2018-12-27,112.46,118.79,113.91,113.98,6331524,114.69,119.05,113.78,112.63,6305428,2.49,2.252,\"Dec 27, 18\",-0.082403\n2018-12-28,114.64,114.67,117.9,115.5,5263463,115.86,115.56,119.9,117.3,5140623,-0.78,-0.6902,\"Dec 28, 18\",-0.086812\n2018-12-31,114.11,114.52,118.06,114.7,5189454,114.59,118.76,115.94,115.13,5207207,0.67,0.5785,\"Dec 31, 18\",-0.081153\n2019-01-02,115.14,120,120.26,115.88,4360829,112.46,117.8,117.97,114.53,4364968,1.61,1.3657,\"Jan 2, 19\",-0.068412\n2019-01-03,117.81,114.51,115.2,113.73,4391478,119.41,114.26,119.32,113.16,4537538,-2.3,-2.0589,\"Jan 3, 19\",-0.089783\n2019-01-04,119.5,123.13,119.17,117.36,4698955,119.15,119.97,123.17,119.79,4669111,4.43,4.0933,\"Jan 4, 19\",-0.050767\n2019-01-07,122.4,120.11,120.06,120.36,3818933,120.7,120.38,119.67,119.53,3844799,0.83,0.721,\"Jan 7, 19\",-0.04436\n2019-01-08,125.42,120.53,123.44,119.83,4791910,124.38,122.74,124.87,120.21,4915857,1.76,1.4431,\"Jan 8, 19\",-0.03108\n2019-01-09,125.56,126.62,123.9,121.95,3661377,124.83,124.17,121.6,125.01,3775699,0.89,0.72,\"Jan 9, 19\",-0.023336\n2019-01-10,125.85,124.53,127.49,123.83,3935957,120.76,125.92,122.53,121.27,3930829,1.1,0.9206,\"Jan 10, 19\",-0.01466\n2019-01-11,121.98,126.94,124.73,126,3803945,127.3,125.38,124.56,125.3,3882691,-0.34,-0.283,\"Jan 11, 19\",-0.01721\n2019-01-14,121.21,123.61,124.22,124.53,5426076,120.74,121.31,122.08,125.17,5275780,-1.1,-0.9116,\"Jan 14, 19\",-0.02648\n2019-01-15,121.79,122.44,126.25,124.32,3629226,126.54,127.73,124.65,125,3632381,1.38,1.161,\"Jan 15, 19\",-0.014966\n2019-01-16,127.58,123.65,126,126.47,4023924,125.56,124.11,126,126.78,3952975,-0.11,-0.0923,\"Jan 16, 19\",-0.015958\n2019-01-17,121.05,123.67,127.4,125.59,5227298,126.31,125.38,124.9,122.71,5129026,0.59,0.4736,\"Jan 17, 19\",-0.011439\n2019-01-18,124.55,128.23,130.6,125.76,6026368,128.43,123.91,125.56,123.81,6119245,1.65,1.395,\"Jan 18, 19\",0.002286\n2019-01-22,129.3,123.84,128.4,123.63,10067232,127.8,127.69,126.8,125.75,10301995,-1.4,-1.0774,\"Jan 22, 19\",-0.008598\n2019-01-23,135.62,134.78,139,132.55,22078515,137.67,139.14,139,133.3,23018745,10.43,8.5142,\"Jan 23, 19\",0.076404\n2019-01-24,137.72,137.01,133.4,133.8,6564123,132.9,138.2,133.9,135.12,6520443,-0.37,-0.2814,\"Jan 24, 19\",0.07468\n2019-01-25,136.39,134.59,140.61,134.68,5980590,139.4,134.89,137.19,137.98,5746147,1.44,1.1271,\"Jan 25, 19\",0.088469\n2019-01-28,134.7,134.64,135.5,138.73,5395338,136.4,136.7,140.7,135.2,5447827,0.3,0.2251,\"Jan 28, 19\",0.087301\n2019-01-29,137.59,135.39,140.53,134.3,5281536,139.77,139.58,138.81,134,5126693,0.06,0.0451,\"Jan 29, 19\",0.09052\n2019-01-30,137,136.12,136.88,134.36,4612018,134,139.21,137.7,134.67,4547947,0.05,0.0382,\"Jan 30, 19\",0.089552\n2019-01-31,139.46,136.58,138.46,136.99,4944179,139.21,138.13,140.38,136.68,5107261,0.04,0.031,\"Jan 31, 19\",0.090773\n2019-02-01,135.05,135.7,136.6,136.84,3859458,136.32,139.4,141.2,138.48,3981266,-0.33,-0.2442,\"Feb 1, 19\",0.08862\n2019-02-04,138.85,141.58,140.2,136.73,4152986,134.29,138.44,136.7,139.63,4142407,1.12,0.8214,\"Feb 4, 19\",0.095187\n2019-02-05,135.46,137.04,136.87,140.4,5520023,139.44,136.95,140.54,139.43,5629944,0.36,0.2697,\"Feb 5, 19\",0.098498\n2019-02-06,140.98,142.56,143.2,137.55,5118060,137.44,143.12,141.85,135.23,5042437,0.77,0.5898,\"Feb 6, 19\",0.104045\n2019-02-07,134.12,135.08,136.72,138,4444197,134.38,137.75,138.83,133.35,4539837,-3.21,-2.366,\"Feb 7, 19\",0.080306\n2019-02-08,132.6,134.24,135.16,132.25,3282043,132.37,135.32,138.87,134.53,3300685,0.52,0.4047,\"Feb 8, 19\",0.0833\n2019-02-11,139.93,139.35,138.43,140.07,3210326,136.53,134.66,140.59,135.33,3120166,0.29,0.2125,\"Feb 11, 19\",0.085281\n2019-02-12,141.59,138.93,137.6,139.79,3444926,139.95,136.13,138.4,137.93,3366588,2.09,1.5854,\"Feb 12, 19\",0.103106\n2019-02-13,138.83,141.09,140.31,138.56,4368007,140.18,141.64,138.38,136.47,4420300,1.52,1.0868,\"Feb 13, 19\",0.118527\n2019-02-14,141.28,138.47,137.7,139.31,2910820,142.66,137.14,140,141.19,2863426,-1.06,-0.7816,\"Feb 14, 19\",0.107777\n2019-02-15,140.44,140.41,144.33,141.64,4007778,141.87,138.87,141.85,144.19,3923816,1.62,1.1699,\"Feb 15, 19\",0.12247\n2019-02-19,138.14,140,144.6,138.15,3454240,144.56,143,141.5,142.99,3460500,0.7,0.4971,\"Feb 19, 19\",0.128788\n2019-02-20,139.25,139,144.06,143.15,3920744,143.29,140,143.73,143.72,3809031,-0.7,-0.5291,\"Feb 20, 19\",0.118144\n2019-02-21,143.32,142.77,142.52,142.71,3054518,141.2,138.68,142.72,139.71,3082805,-0.16,-0.1194,\"Feb 21, 19\",0.118131\n2019-02-22,139.33,144.72,145.16,142.03,3163896,143.99,139.62,140.43,141.62,3239806,1.41,1.0301,\"Feb 22, 19\",0.13188\n2019-02-25,142,139.72,144.05,140,3324204,141,142.11,145.02,140.35,3348401,0.22,0.1575,\"Feb 25, 19\",0.134518\n2019-02-26,141.82,144.51,146.71,140.36,3117913,141.2,142.56,141.38,143.34,3096269,0.27,0.1901,\"Feb 26, 19\",0.13508\n2019-02-27,139.56,145.35,146.52,145.2,2581767,141.85,140.69,140.21,142.6,2640688,-0.56,-0.3968,\"Feb 27, 19\",0.128573\n2019-02-28,140.14,142.56,145.09,144.55,3575178,143.82,142.52,144.69,138.05,3586754,-1.06,-0.7685,\"Feb 28, 19\",0.121035\n2019-03-01,140.12,140.9,141.43,145.06,3146767,143.17,141.9,143.82,141.33,3068983,1.08,0.8066,\"Mar 1, 19\",0.128865\n2019-03-04,141.55,144.87,142.68,140.35,3510481,143.16,142.05,144.21,138.76,3504606,-0.78,-0.5769,\"Mar 4, 19\",0.126059\n2019-03-05,138.41,139.72,145.63,143.86,4385773,140.43,140.46,139.44,142.51,4348815,-0.56,-0.3983,\"Mar 5, 19\",0.119102\n2019-03-06,141.69,140.83,143.54,138.61,2887836,143.28,137.38,139.66,141.74,2795312,-0.9,-0.653,\"Mar 6, 19\",0.113771\n2019-03-07,137.37,140.74,141.32,137.4,4360720,141.1,136.67,138.71,137.56,4559093,-1.65,-1.2092,\"Mar 7, 19\",0.098755\n2019-03-08,136.37,139.56,136.61,133.92,3925282,140.59,140.33,141.48,138.84,3777787,-0.28,-0.2007,\"Mar 8, 19\",0.096489\n2019-03-11,138.27,139.51,143.56,138.95,4104593,136.47,140.68,139.53,141.3,3984452,2.63,1.9589,\"Mar 11, 19\",0.1153\n2019-03-12,145.12,140.78,145.49,144.14,4028985,142.62,140.5,144.83,142.67,4036165,0.57,0.4224,\"Mar 12, 19\",0.119877\n2019-03-13,139.69,142.58,142.12,139.99,3297647,144.66,142.92,143.18,144.53,3281507,0.28,0.21,\"Mar 13, 19\",0.12204\n2019-03-14,143.87,139.65,139.17,144.68,2852781,138.59,139.31,139.46,141.49,2842693,0.24,0.168,\"Mar 14, 19\",0.125579\n2019-03-15,139.93,146.02,143.11,140.89,7305877,141.46,145.33,143.26,143.99,7171981,0.65,0.475,\"Mar 15, 19\",0.128701\n2019-03-18,140.55,143.64,144.68,142.29,3274756,142.6,146.06,146.7,141.61,3426355,0.81,0.5671,\"Mar 18, 19\",0.140928\n2019-03-19,143.23,145.17,148.4,144,3652575,141.81,146.95,143.7,144,3636023,0.28,0.2048,\"Mar 19, 19\",0.14037\n2019-03-20,147.27,140.9,144.6,141.45,3688107,145.25,146.1,140.7,143.08,3731650,-0.92,-0.641,\"Mar 20, 19\",0.135446\n2019-03-21,144.3,146.65,148.31,144.95,3713352,145.7,146.3,146.99,139.63,3744315,1.87,1.3298,\"Mar 21, 19\",0.146399\n2019-03-22,146.44,139.67,142.77,144.4,3918137,145.79,140.97,147.97,142.5,4051513,-2.05,-1.437,\"Mar 22, 19\",0.130247\n2019-03-25,142.05,141.37,143.71,144.99,2925429,139.26,144.53,142.12,144.98,2980674,-0.27,-0.2002,\"Mar 25, 19\",0.129354\n2019-03-26,141.59,147.22,143.2,141.96,2635125,146.84,144.8,146.06,144.35,2566685,1.09,0.7742,\"Mar 26, 19\",0.137619\n2019-03-27,145.79,145.23,141.86,144.8,3233834,143.88,141.02,142.63,141.5,3104163,-1.01,-0.7065,\"Mar 27, 19\",0.12978\n2019-03-28,144.18,144.68,145.84,143.9,2734169,140.92,144.67,141.82,140.5,2693294,0.69,0.4993,\"Mar 28, 19\",0.136644\n2019-03-29,143.2,143.2,146.56,146.99,3237457,142.9,144.2,142.44,140.93,3125461,1.19,0.8746,\"Mar 29, 19\",0.14285\n2019-04-01,144.02,144.1,149.9,143.03,4091224,141.96,144.9,148.33,147.52,4006059,2.2,1.6334,\"Apr 1, 19\",0.164589\n2019-04-02,145.85,144,150.78,143.8,2450096,144.39,146,147.04,143.8,2476866,-0.3,-0.2124,\"Apr 2, 19\",0.16475\n2019-04-03,149.21,148.85,145.66,147.69,2699286,146.85,147.57,149.99,149.43,2717189,0.66,0.4422,\"Apr 3, 19\",0.163587\n2019-04-04,149.92,148.1,148.42,146.9,2834668,144.79,143.6,148.55,149.14,2874806,-0.88,-0.5952,\"Apr 4, 19\",0.157685\n2019-04-05,145.06,148.88,143.8,148.67,2745544,150.13,150.01,147.9,143.28,2801430,0.5,0.3532,\"Apr 5, 19\",0.167052\n2019-04-08,147.09,149.69,148.08,145.12,2187861,143.5,145.28,146.21,147.73,2133098,0.11,0.0771,\"Apr 8, 19\",0.168081\n2019-04-09,145.36,146.95,149.97,142.25,3189772,144.64,146.22,146.73,143.28,3141104,-1.33,-0.8994,\"Apr 9, 19\",0.154317\n2019-04-10,142.9,149.31,146.35,146.17,2684909,146.5,145.81,148.11,143.41,2698822,0.94,0.6591,\"Apr 10, 19\",0.164282\n2019-04-11,144.3,146.75,147.56,144.11,3009435,147,150.12,150.87,146.69,2955012,0.77,0.5389,\"Apr 11, 19\",0.16875\n2019-04-12,150.32,145.32,149.77,145.4,3042671,148.24,150.83,151.15,147.7,2999947,0.57,0.4022,\"Apr 12, 19\",0.174919\n2019-04-15,150.3,149.7,146.07,147.81,3607625,147.2,146.3,149.83,145.53,3669487,-0.46,-0.3177,\"Apr 15, 19\",0.171914\n2019-04-16,145.3,149.42,150.44,144.87,6481493,147.5,151.81,147.93,147.58,6456697,1.3,0.8653,\"Apr 16, 19\",0.179349\n2019-04-17,142.72,143.81,147.33,139.8,13145487,137.46,141.77,148.46,141.4,12864053,-6.2,-4.3192,\"Apr 17, 19\",0.131655\n2019-04-18,143,141.35,145.38,145.6,5071049,143,145.53,142.5,142.1,5172449,1.25,0.884,\"Apr 18, 19\",0.139033\n2019-04-22,142.53,142.53,143.66,139.42,4698417,145.46,144.5,142.36,142.23,4536711,-1.51,-1.0637,\"Apr 22, 19\",0.128778\n2019-04-23,144.67,142.26,144.27,140.7,4490332,139.77,144.95,143.34,140.09,4599074,1.56,1.17,\"Apr 23, 19\",0.138158\n2019-04-24,147.2,141.75,148.37,145.57,2868481,141.5,143.28,142.92,146.27,2847131,-0.49,-0.366,\"Apr 24, 19\",0.137089\n2019-04-25,141.9,141.14,140.81,141.81,2935279,145.2,143.52,139.84,141.96,3016550,-1.38,-0.9887,\"Apr 25, 19\",0.127612\n2019-04-26,143.32,139.79,142.69,141.16,2387920,145.06,144.47,145.42,139.98,2387807,0.82,0.5847,\"Apr 26, 19\",0.133894\n2019-04-29,144.19,142.29,141.49,141.05,3088108,144.52,143.05,142.68,144.47,3051950,-0.4,-0.2827,\"Apr 29, 19\",0.127139\n2019-04-30,140.78,142.36,147.33,144.46,4868695,139.54,145.69,142.31,145.12,4806965,1.24,0.8865,\"Apr 30, 19\",0.137765\n2019-05-01,147.56,141.91,148.06,145.73,3181718,140.89,145.58,142.69,143.65,3054422,0.29,0.208,\"May 1, 19\",0.140544\n2019-05-02,141.1,141.17,147.62,139.85,3646665,141.3,142.46,142.56,143.42,3699376,-1.01,-0.705,\"May 2, 19\",0.130282\n2019-05-03,146.3,142.5,144,140.35,2641265,143.7,144.5,146,145.78,2673023,0.67,0.4879,\"May 3, 19\",0.14085\n2019-05-06,140.8,144.67,144.37,138.5,2838287,143.1,142.65,145.47,138.5,2896019,0.14,0.0968,\"May 6, 19\",0.139598\n2019-05-07,142.03,139.99,139.89,141.35,4858343,143.72,143.29,144.69,139.04,4751170,-2.76,-1.9912,\"May 7, 19\",0.119467\n2019-05-08,140.05,141,143.4,138.38,3887322,138.26,142,138.9,139.72,3841843,0.37,0.2673,\"May 8, 19\",0.121884\n2019-05-09,136.23,137.46,139.29,136.79,4311419,139.57,141.58,141.15,138.98,4324925,-2.78,-1.9863,\"May 9, 19\",0.099688\n2019-05-10,140.31,139.12,138.14,132.48,4106197,135.91,141.47,138.74,135.69,4093123,-0.02,-0.0152,\"May 10, 19\",0.100009\n2019-05-13,139.26,135.54,140.19,131.6,5158280,135.23,134.68,139.95,136.49,5033580,-3.9,-2.8862,\"May 13, 19\",0.066196\n2019-05-14,137.83,134.2,136.61,135.96,3411384,136.6,133.42,140.25,136.83,3403798,1.98,1.4822,\"May 14, 19\",0.082835\n2019-05-15,135.57,134.6,138.92,135.58,2555858,138.77,139.8,137.43,134.4,2618809,1.09,0.8192,\"May 15, 19\",0.091416\n2019-05-16,135.67,138.33,142.43,135.92,3089045,140.14,140.66,138.36,134.72,3068462,1.49,1.1462,\"May 16, 19\",0.101835\n2019-05-17,138.09,135.41,141.64,138.9,2686345,140.35,134.52,135.87,134.34,2677351,-1.63,-1.1897,\"May 17, 19\",0.089613\n2019-05-20,136.41,140.64,138.56,137.15,3287990,138.58,141.75,139.79,137.89,3386000,0.8,0.615,\"May 20, 19\",0.097504\n2019-05-21,138.48,141.52,141.72,139.15,2508704,140.34,138.22,137.84,137.61,2569715,1.35,1.0018,\"May 21, 19\",0.108951\n2019-05-22,143,141.36,137.62,140.54,1934021,139,141.73,141.48,138.31,1901989,-0.1,-0.0754,\"May 22, 19\",0.104306\n2019-05-23,137.47,134,140.35,136.96,5495102,140.07,133.31,138.41,133.62,5417426,-4.08,-2.9198,\"May 23, 19\",0.073115\n2019-05-24,139.17,134.31,139.13,136.53,2649743,139.16,138.21,137.99,136.6,2635339,-0.11,-0.0858,\"May 24, 19\",0.073663\n2019-05-28,138.07,132.13,137.25,132.8,4912544,138.49,134.19,138.87,133.58,4970826,-1.9,-1.4262,\"May 28, 19\",0.058519\n2019-05-29,131,131.26,131.02,133.63,3742234,132,133.97,135.9,132.4,3821945,-0.78,-0.608,\"May 29, 19\",0.051267\n2019-05-30,131.05,132.71,131.37,130.71,2806880,134.35,134.25,135.31,130.47,2770175,-0.12,-0.0944,\"May 30, 19\",0.04903\n2019-05-31,131.55,131.7,130.84,132.23,3664262,129.76,131.3,130.49,127.25,3663986,-2.61,-2.0071,\"May 31, 19\",0.029217\n2019-06-03,132.3,128.48,129.72,131.86,4360833,132.2,132.09,132.75,133.17,4287644,1.29,1.027,\"Jun 3, 19\",0.039983\n2019-06-04,132.34,134.36,139.01,131,3949381,130.03,134.18,135.82,135.4,3973499,4.52,3.4876,\"Jun 4, 19\",0.075516\n2019-06-05,138.29,137.08,135.62,130.66,3297131,139.35,137.24,138.73,130.47,3221092,-1.2,-0.907,\"Jun 5, 19\",0.064383\n2019-06-06,133.72,136.78,139.18,131.4,2352926,135.57,133.32,134.06,131.6,2410194,0.76,0.558,\"Jun 6, 19\",0.073261\n2019-06-07,135.37,137.86,140.97,132.41,2565927,134.53,138.16,135.86,137.25,2632591,1.13,0.8402,\"Jun 7, 19\",0.079881\n2019-06-10,138.18,141.19,137.12,139.75,3013566,134.84,137.54,138.85,134.11,2975177,1.45,1.1161,\"Jun 10, 19\",0.092139\n2019-06-11,140.89,137.79,139.08,140.97,3555345,141.44,137.29,143.25,139.71,3536103,1.22,0.91,\"Jun 11, 19\",0.105457\n2019-06-12,142.29,137.9,140.07,141.08,2264751,140.67,139.97,136.02,140.68,2345613,-1.1,-0.8322,\"Jun 12, 19\",0.093299\n2019-06-13,135.97,137.05,136.29,137.26,2983515,140.23,136.86,142.87,140.37,3069394,0.89,0.6649,\"Jun 13, 19\",0.099866\n2019-06-14,140.31,136.71,140.5,136.01,2273481,137.39,135.87,140.25,135.87,2253979,-0.63,-0.4648,\"Jun 14, 19\",0.095516\n2019-06-17,136.07,139.03,140.5,138.4,2298620,140.21,141.68,139.55,137.4,2195604,-0.2,-0.152,\"Jun 17, 19\",0.092392\n2019-06-18,140.1,142.62,142.37,140.4,3037826,136.9,142.58,140.42,141.8,2985688,1.49,1.1122,\"Jun 18, 19\",0.106029\n2019-06-19,141.91,138.79,140.57,137.44,2501540,137.45,143.69,145.12,139.68,2405193,0.7,0.5221,\"Jun 19, 19\",0.1151\n2019-06-20,145.77,139.51,146.51,143.7,3142257,143.72,139.18,140.5,144.2,3077748,1.85,1.3093,\"Jun 20, 19\",0.124501\n2019-06-21,143.78,143.4,143.71,144.26,5419027,138.83,142.8,140.35,143.73,5425542,0.37,0.2621,\"Jun 21, 19\",0.129465\n2019-06-24,139.3,140.74,141.82,144.51,2236096,139.2,144.01,143.12,143.62,2242039,0.15,0.1101,\"Jun 24, 19\",0.132545\n2019-06-25,145.61,144.37,140.77,143.94,2912960,139.91,143.38,139.9,141,2801770,-0.99,-0.7272,\"Jun 25, 19\",0.123822\n2019-06-26,140.05,143.63,140.23,138.7,2313785,145.35,141.51,139.77,141.1,2375847,0.17,0.1172,\"Jun 26, 19\",0.121735\n2019-06-27,140.51,139.29,145.4,139.99,2181515,140.5,138.86,139.9,143.31,2254873,0,0,\"Jun 27, 19\",0.125066\n2019-06-28,139.73,141.6,143.99,138.1,6394657,140.12,141.1,140.06,143.45,6312037,-0.63,-0.4642,\"Jun 28, 19\",0.119994\n2019-07-01,145.3,146.65,147.17,140.54,3486823,140.1,142.21,145.8,145.32,3464427,2.05,1.4839,\"Jul 1, 19\",0.138538\n2019-07-02,143.21,143.98,147.4,145.69,2452836,146.27,145.22,142.74,139.52,2451943,0.35,0.2524,\"Jul 2, 19\",0.137144\n2019-07-03,144.74,147.24,145.02,142.9,1962457,146.08,144.12,144.4,143.43,2024682,1.33,0.9506,\"Jul 3, 19\",0.148383\n2019-07-05,147.56,144.66,144.71,144.35,2142875,142.35,146.81,143.47,143.37,2222984,-0.17,-0.116,\"Jul 5, 19\",0.148245\n2019-07-08,144.13,146.21,144.06,141.97,2487668,144,142.48,146.42,143.93,2497757,-0.84,-0.5837,\"Jul 8, 19\",0.13972\n2019-07-09,145.54,144.67,146.51,145.7,3422256,146.07,144.99,147.76,141.23,3414167,-1.27,-0.8853,\"Jul 9, 19\",0.132363\n2019-07-10,142,143.36,148.39,140.44,2617885,142,141.17,146.74,143.34,2570862,1.17,0.8351,\"Jul 10, 19\",0.137046\n2019-07-11,147.58,144.97,145.18,144.4,2221214,145,146.94,147.67,146.4,2144072,0.78,0.5584,\"Jul 11, 19\",0.145396\n2019-07-12,145.84,142.97,145.94,141.84,2845291,142.39,149.22,143.65,142.11,2787523,1.6,1.08,\"Jul 12, 19\",0.158434\n2019-07-15,146.64,143.94,149.33,147.68,2479503,149.44,148.45,146.16,142.19,2415125,0.59,0.4208,\"Jul 15, 19\",0.16028\n2019-07-16,147.35,149.94,151.23,143.78,3526204,145.59,147.76,147.68,144.13,3644592,0.22,0.1522,\"Jul 16, 19\",0.16721\n2019-07-17,144.82,143.89,148.3,148.39,6330191,149.01,149.05,145,148.03,6223183,-0.47,-0.3306,\"Jul 17, 19\",0.159591\n2019-07-18,143.5,152.35,152.77,145.3,13186135,144.7,152.34,154.71,146.3,12997321,6.65,4.63,\"Jul 18, 19\",0.215753\n2019-07-19,155.51,151.69,157.13,156.52,7314784,151.4,155.08,155.3,151.45,7637759,0.05,0.0335,\"Jul 19, 19\",0.215984\n2019-07-22,157.19,155.76,154.26,150.3,4216943,154.35,155.08,159.18,151.4,4229003,0.06,0.0408,\"Jul 22, 19\",0.219043\n2019-07-23,152.7,151.82,155.93,153.42,3884831,154.1,152.55,157.46,152.95,3923811,0.65,0.4372,\"Jul 23, 19\",0.228023\n2019-07-24,151.53,156.03,151.78,149.21,3029448,152.13,155.54,158.93,149.69,3002426,-0.36,-0.2334,\"Jul 24, 19\",0.217455\n2019-07-25,151.64,157.61,154.83,154.32,3103762,150.15,155.86,155.75,149.77,3234778,0.38,0.25,\"Jul 25, 19\",0.219249\n2019-07-26,158,158.61,153.95,155.8,2963091,157,157.45,158.18,153.5,2958891,0.99,0.649,\"Jul 26, 19\",0.22679\n2019-07-29,157.73,151.88,151.73,156.33,2170966,153.55,153.92,158.42,153.72,2081200,-0.49,-0.3324,\"Jul 29, 19\",0.222123\n2019-07-30,155,155.87,155.8,153.46,2694358,151,151.64,157,156.14,2640246,-1.14,-0.7596,\"Jul 30, 19\",0.22101\n2019-07-31,149.8,150.51,153.6,150.97,3905923,156.8,151.19,153.4,150.18,3901269,-1.57,-1.0315,\"Jul 31, 19\",0.206561\n2019-08-01,152.5,157.72,160.29,153.2,6569105,150.2,152.31,154.42,153.7,6508657,2.17,1.4189,\"Aug 1, 19\",0.222808\n2019-08-02,153.66,152.09,159.28,152.63,8284978,151.87,150.63,155.47,147.77,8210012,-3.12,-2.0835,\"Aug 2, 19\",0.198828\n2019-08-05,149.98,145.37,151.2,144.45,7284195,147.55,141.69,152.3,140.01,7118079,-6.8,-4.504,\"Aug 5, 19\",0.146333\n2019-08-06,143.43,147.55,142.95,142.94,5152918,144.67,146.61,146.91,142.69,5205144,-0.03,-0.0221,\"Aug 6, 19\",0.145272\n2019-08-07,139.8,145.5,142.47,142.03,6080793,140.59,145.92,145.87,140,5997339,-1.62,-1.2076,\"Aug 7, 19\",0.128501\n2019-08-08,139.62,143.3,142.81,138.77,5513457,139.01,144.2,145.19,140.15,5452898,1.01,0.7146,\"Aug 8, 19\",0.137654\n2019-08-09,141.58,137.16,142.67,141.82,5342470,141.26,141.37,140.98,136.67,5361175,-4.17,-2.9643,\"Aug 9, 19\",0.10432\n2019-08-12,136.72,138.75,141.82,133.58,4064544,141.39,138.88,138.22,137.33,4249728,-2.06,-1.5009,\"Aug 12, 19\",0.08646\n2019-08-13,136.4,141.87,141.84,132.8,4706038,139.5,140.38,142.08,134.5,4593034,1.75,1.2625,\"Aug 13, 19\",0.099632\n2019-08-14,139.14,133.17,139.61,131.34,5233225,137.96,136.64,136.46,134.44,5150938,-4.75,-3.4208,\"Aug 14, 19\",0.063308\n2019-08-15,133.98,132.32,134.82,131.06,3876899,135.95,136.04,132.37,134.32,3744787,0.69,0.5218,\"Aug 15, 19\",0.06831\n2019-08-16,133.91,137.96,140.34,137.41,3134536,136.84,136.13,134.7,133.97,3117684,1.92,1.4497,\"Aug 16, 19\",0.085215\n2019-08-19,136.69,135.94,142.52,141.6,3198137,138.55,136.62,136.81,140.66,3210403,1.31,0.9782,\"Aug 19, 19\",0.097219\n2019-08-20,141.59,134,141.06,137.3,3134754,139.79,139,140.11,133.1,3082163,-2.05,-1.5647,\"Aug 20, 19\",0.076621\n2019-08-21,139.67,139.58,141.35,136.7,2908148,139.65,135.97,142.33,139.4,2881172,1.29,0.9855,\"Aug 21, 19\",0.090521\n2019-08-22,137.85,135.13,141.06,135.22,2765978,134.77,136.07,138.48,135.84,2706509,0.07,0.0525,\"Aug 22, 19\",0.087915\n2019-08-23,134.82,134.97,135.46,133.22,4960569,136.92,131.65,140.88,134.26,5014948,-4.92,-3.5785,\"Aug 23, 19\",0.05066\n2019-08-26,134.2,134.26,136.8,129.93,2874506,134.86,135.08,132.9,131.99,2859240,0.43,0.3394,\"Aug 26, 19\",0.05398\n2019-08-27,133.8,131.29,136.78,133.02,4807652,135.8,137.37,135.47,131.84,4758741,1.19,0.9183,\"Aug 27, 19\",0.06354\n2019-08-28,132.99,138.74,136.84,134.48,2854838,134.18,135.7,134.42,131.58,2787370,1.61,1.2711,\"Aug 28, 19\",0.077647\n2019-08-29,135.19,137.88,142.14,138.35,3031300,136.75,135.9,137.72,139,3070896,2.22,1.6739,\"Aug 29, 19\",0.094363\n2019-08-30,137.67,141.29,138.54,136.88,2962892,142.02,140.85,140.97,138.95,3004116,0.66,0.4831,\"Aug 30, 19\",0.099415\n2019-09-03,139.63,138,135.2,136.57,2882297,136.07,137.6,141.1,136.64,2893069,-1.5,-1.0677,\"Sep 3, 19\",0.089056\n2019-09-04,137.87,136.76,138.81,138.41,2321402,137.58,140.58,143.12,140.35,2273544,2.28,1.7202,\"Sep 4, 19\",0.107869\n2019-09-05,144.9,146.18,144.79,141.22,5116677,139.2,145.1,143.35,142.69,5017581,4.65,3.5431,\"Sep 5, 19\",0.14409\n2019-09-06,146.01,143.64,144.78,140.56,2665286,142.66,143.22,144.37,147.47,2610802,-0.4,-0.2947,\"Sep 6, 19\",0.14427\n2019-09-09,141.45,144.7,147.27,146.21,4266706,146.93,142.7,143.44,144.54,4384199,2.06,1.5036,\"Sep 9, 19\",0.15816\n2019-09-10,150,145.55,149,144.33,5158854,147,146.59,146.45,148.1,4999252,2.46,1.7676,\"Sep 10, 19\",0.177138\n2019-09-11,145.91,147.2,146.11,145.9,4044328,151.92,149.9,149.52,148.4,3989656,-1.51,-1.024,\"Sep 11, 19\",0.165425\n2019-09-12,150.96,147.95,149.6,142.68,2538728,145.13,147.93,144.9,146.76,2533211,0.02,0.0146,\"Sep 12, 19\",0.163508\n2019-09-13,147.32,149.04,146.23,150.3,2284295,151.4,143.87,145.7,148.03,2211303,0.05,0.0353,\"Sep 13, 19\",0.165547\n2019-09-16,147.75,149.13,148.15,148.88,2002324,142.94,148.78,145.62,143.9,1949443,-1.19,-0.8408,\"Sep 16, 19\",0.156143\n2019-09-17,148.55,146.8,148.19,145.14,3033687,145.58,146.7,143.02,143.37,3064118,-0.29,-0.2002,\"Sep 17, 19\",0.156194\n2019-09-18,148.34,144.46,144.7,141.23,1996440,144.52,143.58,148.6,141.18,2001179,0.02,0.0146,\"Sep 18, 19\",0.152456\n2019-09-19,145.26,145.32,145.21,143.95,3261603,147.72,147.42,148.19,147.51,3243256,0.76,0.5515,\"Sep 19, 19\",0.159521\n2019-09-20,146.04,146.55,150.97,147.04,5238272,148.78,147.64,148.53,142.81,5454281,-1.12,-0.7798,\"Sep 20, 19\",0.15124\n2019-09-23,144.34,143.04,142.53,143.75,1937751,147.43,148.75,142.24,144.97,1881284,0.2,0.136,\"Sep 23, 19\",0.154787\n2019-09-24,143,146.54,147.37,146.83,3395808,143.7,145.87,146.74,141.82,3462617,-0.4,-0.2788,\"Sep 24, 19\",0.148238\n2019-09-25,147.94,148.36,148.69,144.11,2614193,144.36,146.5,145.44,147.18,2648266,1.52,1.0714,\"Sep 25, 19\",0.158815\n2019-09-26,147.18,145.78,148.01,144.17,2430128,147.38,149.93,147.28,141.95,2367608,0.39,0.2738,\"Sep 26, 19\",0.16907\n2019-09-27,149.96,143.73,148.84,148.56,2368044,150.46,147,150.47,146.15,2381776,-0.32,-0.225,\"Sep 27, 19\",0.165878\n2019-09-30,149.01,149.23,150.91,148.96,4050527,147.91,152.47,147.43,147.87,4153451,2.26,1.5881,\"Sep 30, 19\",0.179768\n2019-10-01,145.82,144.17,148.13,144.86,3091359,147.61,144.44,149.09,147.59,3094349,-1.82,-1.2242,\"Oct 1, 19\",0.170617\n2019-10-02,147.81,147.89,144.38,145.88,3417539,142.71,145.8,149.16,144.96,3444235,-1.99,-1.3743,\"Oct 2, 19\",0.151432\n2019-10-03,142.98,142.54,148.71,141.62,3226478,141.87,146.23,144.44,141.17,3193966,0.33,0.2385,\"Oct 3, 19\",0.154196\n2019-10-04,144.98,144.61,147.51,142.46,2562258,142.36,149.68,146.85,141.44,2516786,1,0.695,\"Oct 4, 19\",0.16502\n2019-10-07,142.81,148.2,147.3,146.31,2584330,143.78,141.99,147.5,145.68,2610602,-1.72,-1.2151,\"Oct 7, 19\",0.147504\n2019-10-08,146.46,141.02,142.6,138.75,3490486,141.44,143.66,145.23,144.42,3471810,-3,-2.0572,\"Oct 8, 19\",0.121411\n2019-10-09,142.69,144.4,142.13,139.24,2658178,146.61,145.95,142.81,141.13,2693567,1.35,0.9381,\"Oct 9, 19\",0.130603\n2019-10-10,144.4,143.74,145.49,146.15,2708927,145.62,143.39,144.27,143.36,2733405,1.51,1.082,\"Oct 10, 19\",0.145596\n2019-10-11,143.14,144.62,150.9,142.64,3145794,145.19,143.32,151.5,142.7,3137631,1.68,1.195,\"Oct 11, 19\",0.15938\n2019-10-14,145.66,143.37,145.51,148.22,2400013,143.91,145.75,147.82,146.15,2433423,-0.72,-0.5135,\"Oct 14, 19\",0.151243\n2019-10-15,147.95,147,149.09,142.05,3173358,142.57,148,149.89,146.16,3098918,0.97,0.6824,\"Oct 15, 19\",0.15769\n2019-10-16,147.56,148.12,144.5,141.71,6196731,143.35,148.65,145.83,146.82,6037152,-0.92,-0.6243,\"Oct 16, 19\",0.151583\n2019-10-17,136,138.26,138,134.58,16373893,136,137.61,138,136.01,16124374,-8.02,-5.565,\"Oct 17, 19\",0.089641\n2019-10-18,140.23,134.19,136.92,137.05,7411626,140.43,134.1,140.4,135.41,7377351,-0.17,-0.1282,\"Oct 18, 19\",0.085889\n2019-10-21,135.93,135.08,138.7,132.6,6643994,139.18,138.34,138.2,132.4,6525178,-1.53,-1.1716,\"Oct 21, 19\",0.074142\n2019-10-22,133.06,139.48,139.28,138.17,4232511,133.85,135.56,135.91,137,4392755,1.42,1.0665,\"Oct 22, 19\",0.088524\n2019-10-23,134.75,139.51,137.87,137,3679128,135.6,139.11,138.81,134.3,3753523,0.44,0.3195,\"Oct 23, 19\",0.090169\n2019-10-24,140.91,139.97,139.98,135.6,2652223,139.54,135.94,139.78,133.8,2692173,-0.32,-0.2362,\"Oct 24, 19\",0.088343\n2019-10-25,140.4,141.14,136.41,138.8,2683588,139.2,139.69,140.77,137.7,2668571,1.41,1.0608,\"Oct 25, 19\",0.097756\n2019-10-28,140,139.3,138.11,139.11,3252032,137,137.98,141.94,138.38,3312733,0.55,0.4,\"Oct 28, 19\",0.103372\n2019-10-29,140.07,138.3,136.01,133.79,4315419,135.68,136.46,140.8,136.09,4190177,-2.22,-1.6306,\"Oct 29, 19\",0.084322\n2019-10-30,138.26,135.64,136.63,134,2361383,133.99,138.02,141.07,134.6,2345588,1.44,1.1027,\"Oct 30, 19\",0.098219\n2019-10-31,138.29,139.36,141.62,135.65,3519134,136.1,134.21,141.49,137.48,3457696,-1.54,-1.1479,\"Oct 31, 19\",0.085539\n2019-11-01,140.8,137.64,139.53,138.59,3218745,136.4,137.24,136.23,137.62,3194671,1.9,1.349,\"Nov 1, 19\",0.099839\n2019-11-04,137.25,142.51,140.66,137.38,3460689,140.45,142.73,142.46,138.43,3346654,2.25,1.587,\"Nov 4, 19\",0.117226\n2019-11-05,140.8,138.06,140.05,140.95,3089196,142,138.22,143.4,141.02,3107756,0.22,0.1632,\"Nov 5, 19\",0.121931\n2019-11-06,142,139.56,144.5,137.95,4651097,142,141.21,141.69,141.12,4586632,0.91,0.6512,\"Nov 6, 19\",0.127362\n2019-11-07,138.11,142.63,141.91,137.68,4240594,144.34,142.17,142.53,141.96,4129159,-1.14,-0.8166,\"Nov 7, 19\",0.116692\n2019-11-08,144.32,144.22,139.94,137.5,2283100,139.91,141.26,144.25,138.05,2355641,-0.08,-0.0583,\"Nov 8, 19\",0.11865"
  },
  {
    "path": "jgnash-tests/src/test/resources/IEX-IBM-1y.json",
    "content": "[{\"date\":\"2018-11-09\",\"open\":127.89,\"close\":123.59,\"high\":124.6,\"low\":123.78,\"volume\":6993091,\"uOpen\":124.91,\"uClose\":128.05,\"uHigh\":129.76,\"uLow\":127.9,\"uVolume\":7058168,\"change\":0,\"changePercent\":0,\"label\":\"Nov 9, 18\",\"changeOverTime\":0},{\"date\":\"2018-11-12\",\"open\":123.6,\"close\":125.7,\"high\":126.13,\"low\":121.87,\"volume\":5503376,\"uOpen\":127.9,\"uClose\":123.2,\"uHigh\":124.76,\"uLow\":122.76,\"uVolume\":5639797,\"change\":-2.73,\"changePercent\":-2.161,\"label\":\"Nov 12, 18\",\"changeOverTime\":-0.02177},{\"date\":\"2018-11-13\",\"open\":125.57,\"close\":125.68,\"high\":124.72,\"low\":121.6,\"volume\":4173520,\"uOpen\":122.57,\"uClose\":123.3,\"uHigh\":123.47,\"uLow\":125.7,\"uVolume\":4122985,\"change\":-0.06,\"changePercent\":-0.0512,\"label\":\"Nov 13, 18\",\"changeOverTime\":-0.02238},{\"date\":\"2018-11-14\",\"open\":126.72,\"close\":121.6,\"high\":126.18,\"low\":120.36,\"volume\":4788072,\"uOpen\":124.74,\"uClose\":122.5,\"uHigh\":125.7,\"uLow\":123.07,\"uVolume\":4867573,\"change\":-0.64,\"changePercent\":-0.5333,\"label\":\"Nov 14, 18\",\"changeOverTime\":-0.027597},{\"date\":\"2018-11-15\",\"open\":121.82,\"close\":121.8,\"high\":122.09,\"low\":124.48,\"volume\":5254030,\"uOpen\":124.67,\"uClose\":122.25,\"uHigh\":127.05,\"uLow\":121.34,\"uVolume\":5391772,\"change\":1.27,\"changePercent\":1.044,\"label\":\"Nov 15, 18\",\"changeOverTime\":-0.017787},{\"date\":\"2018-11-16\",\"open\":125.44,\"close\":123.64,\"high\":127.62,\"low\":122.39,\"volume\":4125432,\"uOpen\":122.22,\"uClose\":124.6,\"uHigh\":125.39,\"uLow\":121.63,\"uVolume\":4131407,\"change\":0.13,\"changePercent\":0.11,\"label\":\"Nov 16, 18\",\"changeOverTime\":-0.016486},{\"date\":\"2018-11-19\",\"open\":123.75,\"close\":121.55,\"high\":125.1,\"low\":124.28,\"volume\":4302446,\"uOpen\":125.5,\"uClose\":121.48,\"uHigh\":123.33,\"uLow\":119.78,\"uVolume\":4166238,\"change\":-1.27,\"changePercent\":-1.0632,\"label\":\"Nov 19, 18\",\"changeOverTime\":-0.026796},{\"date\":\"2018-11-20\",\"open\":122.27,\"close\":122.5,\"high\":120.98,\"low\":120.32,\"volume\":6441466,\"uOpen\":120.29,\"uClose\":121.5,\"uHigh\":120.38,\"uLow\":119.82,\"uVolume\":6357663,\"change\":-3.15,\"changePercent\":-2.641,\"label\":\"Nov 20, 18\",\"changeOverTime\":-0.053558},{\"date\":\"2018-11-21\",\"open\":122.74,\"close\":121.89,\"high\":119.72,\"low\":121.52,\"volume\":5527591,\"uOpen\":118.45,\"uClose\":123.62,\"uHigh\":125.25,\"uLow\":118.09,\"uVolume\":5447754,\"change\":1.41,\"changePercent\":1.2037,\"label\":\"Nov 21, 18\",\"changeOverTime\":-0.04212},{\"date\":\"2018-11-23\",\"open\":120,\"close\":122.89,\"high\":123.89,\"low\":120.09,\"volume\":2356863,\"uOpen\":120.77,\"uClose\":119.43,\"uHigh\":121.3,\"uLow\":119.24,\"uVolume\":2357720,\"change\":-1.42,\"changePercent\":-1.1933,\"label\":\"Nov 23, 18\",\"changeOverTime\":-0.0531},{\"date\":\"2018-11-26\",\"open\":122.56,\"close\":123.04,\"high\":124.66,\"low\":118.61,\"volume\":5133692,\"uOpen\":119.07,\"uClose\":122.6,\"uHigh\":122.86,\"uLow\":118.08,\"uVolume\":5100652,\"change\":2.44,\"changePercent\":2.0519,\"label\":\"Nov 26, 18\",\"changeOverTime\":-0.033733},{\"date\":\"2018-11-27\",\"open\":119.73,\"close\":121.64,\"high\":124.33,\"low\":123.53,\"volume\":4946115,\"uOpen\":118.68,\"uClose\":120.96,\"uHigh\":123.4,\"uLow\":123.94,\"uVolume\":4910478,\"change\":0.47,\"changePercent\":0.4023,\"label\":\"Nov 27, 18\",\"changeOverTime\":-0.029153},{\"date\":\"2018-11-28\",\"open\":124.92,\"close\":126,\"high\":123.94,\"low\":124.15,\"volume\":5183470,\"uOpen\":125.67,\"uClose\":124,\"uHigh\":124.54,\"uLow\":122.06,\"uVolume\":5352704,\"change\":3.06,\"changePercent\":2.5207,\"label\":\"Nov 28, 18\",\"changeOverTime\":-0.004391},{\"date\":\"2018-11-29\",\"open\":125.26,\"close\":125.62,\"high\":128.54,\"low\":122.4,\"volume\":5216140,\"uOpen\":125.75,\"uClose\":125.08,\"uHigh\":124.54,\"uLow\":122.1,\"uVolume\":5443998,\"change\":-1.59,\"changePercent\":-1.2581,\"label\":\"Nov 29, 18\",\"changeOverTime\":-0.017038},{\"date\":\"2018-11-30\",\"open\":127.6,\"close\":126.58,\"high\":126.06,\"low\":125.09,\"volume\":7478794,\"uOpen\":121.81,\"uClose\":126.2,\"uHigh\":127.18,\"uLow\":124.55,\"uVolume\":7344948,\"change\":2.8,\"changePercent\":2.3979,\"label\":\"Nov 30, 18\",\"changeOverTime\":0.006044},{\"date\":\"2018-12-03\",\"open\":129.73,\"close\":125.79,\"high\":130.08,\"low\":124.79,\"volume\":5935811,\"uOpen\":130.78,\"uClose\":128.91,\"uHigh\":127.77,\"uLow\":127.23,\"uVolume\":6076442,\"change\":1.08,\"changePercent\":0.8414,\"label\":\"Dec 3, 18\",\"changeOverTime\":0.014599},{\"date\":\"2018-12-04\",\"open\":124.71,\"close\":123.4,\"high\":127.8,\"low\":124.42,\"volume\":6283138,\"uOpen\":125.89,\"uClose\":122.6,\"uHigh\":128.58,\"uLow\":123.97,\"uVolume\":6248772,\"change\":-3.81,\"changePercent\":-2.9683,\"label\":\"Dec 4, 18\",\"changeOverTime\":-0.015741},{\"date\":\"2018-12-06\",\"open\":125.1,\"close\":128.97,\"high\":128.96,\"low\":123.8,\"volume\":7031271,\"uOpen\":122.87,\"uClose\":127.38,\"uHigh\":125.46,\"uLow\":122.1,\"uVolume\":7101136,\"change\":2.33,\"changePercent\":1.9629,\"label\":\"Dec 6, 18\",\"changeOverTime\":0.0031},{\"date\":\"2018-12-07\",\"open\":125.5,\"close\":120.41,\"high\":127.06,\"low\":121.41,\"volume\":7275478,\"uOpen\":129.7,\"uClose\":123.24,\"uHigh\":129.51,\"uLow\":123.91,\"uVolume\":7111886,\"change\":-4.7,\"changePercent\":-3.6943,\"label\":\"Dec 7, 18\",\"changeOverTime\":-0.034848},{\"date\":\"2018-12-10\",\"open\":123.1,\"close\":126.76,\"high\":126.62,\"low\":123.12,\"volume\":6863457,\"uOpen\":123.23,\"uClose\":126.04,\"uHigh\":123.59,\"uLow\":123.33,\"uVolume\":6806644,\"change\":1.85,\"changePercent\":1.5287,\"label\":\"Dec 10, 18\",\"changeOverTime\":-0.020445},{\"date\":\"2018-12-11\",\"open\":126.86,\"close\":126.31,\"high\":128.86,\"low\":120,\"volume\":5122081,\"uOpen\":126.42,\"uClose\":125.3,\"uHigh\":126.98,\"uLow\":120,\"uVolume\":5268690,\"change\":-0.25,\"changePercent\":-0.1997,\"label\":\"Dec 11, 18\",\"changeOverTime\":-0.022154},{\"date\":\"2018-12-12\",\"open\":127.46,\"close\":125.99,\"high\":123.59,\"low\":124,\"volume\":3689638,\"uOpen\":127.08,\"uClose\":126.2,\"uHigh\":123.64,\"uLow\":122,\"uVolume\":3712221,\"change\":0.28,\"changePercent\":0.2291,\"label\":\"Dec 12, 18\",\"changeOverTime\":-0.019649},{\"date\":\"2018-12-13\",\"open\":123.26,\"close\":124.76,\"high\":126.69,\"low\":120.52,\"volume\":4110170,\"uOpen\":121.63,\"uClose\":124.45,\"uHigh\":127.8,\"uLow\":120.52,\"uVolume\":4150678,\"change\":-0.43,\"changePercent\":-0.3702,\"label\":\"Dec 13, 18\",\"changeOverTime\":-0.022987},{\"date\":\"2018-12-14\",\"open\":122.4,\"close\":120,\"high\":125.79,\"low\":121.43,\"volume\":4601798,\"uOpen\":122.3,\"uClose\":125.3,\"uHigh\":126.7,\"uLow\":123.93,\"uVolume\":4679113,\"change\":-0.83,\"changePercent\":-0.7116,\"label\":\"Dec 14, 18\",\"changeOverTime\":-0.029521},{\"date\":\"2018-12-17\",\"open\":121.55,\"close\":116.8,\"high\":125.45,\"low\":116.47,\"volume\":7540780,\"uOpen\":120.82,\"uClose\":119.2,\"uHigh\":120.47,\"uLow\":120.43,\"uVolume\":7380673,\"change\":-3.8,\"changePercent\":-3.2508,\"label\":\"Dec 17, 18\",\"changeOverTime\":-0.060606},{\"date\":\"2018-12-18\",\"open\":122.7,\"close\":118.12,\"high\":123.63,\"low\":116.33,\"volume\":6501938,\"uOpen\":118.6,\"uClose\":120.42,\"uHigh\":120.54,\"uLow\":118.82,\"uVolume\":6314345,\"change\":0.55,\"changePercent\":0.476,\"label\":\"Dec 18, 18\",\"changeOverTime\":-0.056754},{\"date\":\"2018-12-19\",\"open\":120.36,\"close\":117.16,\"high\":123.96,\"low\":117.47,\"volume\":7203283,\"uOpen\":119.72,\"uClose\":117.26,\"uHigh\":121.27,\"uLow\":120.88,\"uVolume\":7253101,\"change\":-0.23,\"changePercent\":-0.1957,\"label\":\"Dec 19, 18\",\"changeOverTime\":-0.058753},{\"date\":\"2018-12-20\",\"open\":117.8,\"close\":118.5,\"high\":117.75,\"low\":113.4,\"volume\":8471191,\"uOpen\":120.2,\"uClose\":118.02,\"uHigh\":119.39,\"uLow\":116,\"uVolume\":8812387,\"change\":-3.52,\"changePercent\":-3.0173,\"label\":\"Dec 20, 18\",\"changeOverTime\":-0.088018},{\"date\":\"2018-12-21\",\"open\":113.6,\"close\":112.55,\"high\":116.58,\"low\":112.93,\"volume\":10527039,\"uOpen\":113,\"uClose\":116.32,\"uHigh\":119.87,\"uLow\":115.7,\"uVolume\":10855652,\"change\":-2.14,\"changePercent\":-1.877,\"label\":\"Dec 21, 18\",\"changeOverTime\":-0.105591},{\"date\":\"2018-12-24\",\"open\":110,\"close\":108.28,\"high\":112,\"low\":112.8,\"volume\":3922949,\"uOpen\":113,\"uClose\":111.04,\"uHigh\":113,\"uLow\":109.8,\"uVolume\":3839066,\"change\":-3.53,\"changePercent\":-3.1602,\"label\":\"Dec 24, 18\",\"changeOverTime\":-0.13454},{\"date\":\"2018-12-26\",\"open\":111,\"close\":112.88,\"high\":114.07,\"low\":109.87,\"volume\":6954622,\"uOpen\":113,\"uClose\":112.77,\"uHigh\":114.82,\"uLow\":107.06,\"uVolume\":6790566,\"change\":3.93,\"changePercent\":3.6965,\"label\":\"Dec 26, 18\",\"changeOverTime\":-0.101431},{\"date\":\"2018-12-27\",\"open\":111.71,\"close\":115.44,\"high\":118.82,\"low\":114.5,\"volume\":6250970,\"uOpen\":111.11,\"uClose\":118.51,\"uHigh\":118.03,\"uLow\":111.6,\"uVolume\":6093920,\"change\":2.42,\"changePercent\":2.159,\"label\":\"Dec 27, 18\",\"changeOverTime\":-0.079004},{\"date\":\"2018-12-28\",\"open\":116.68,\"close\":117.95,\"high\":118.2,\"low\":115.9,\"volume\":5286949,\"uOpen\":116.64,\"uClose\":114.21,\"uHigh\":115.3,\"uLow\":113.9,\"uVolume\":5327597,\"change\":-0.78,\"changePercent\":-0.6615,\"label\":\"Dec 28, 18\",\"changeOverTime\":-0.089296},{\"date\":\"2018-12-31\",\"open\":118.14,\"close\":115.57,\"high\":118.62,\"low\":115.97,\"volume\":5134462,\"uOpen\":116.61,\"uClose\":118.31,\"uHigh\":117,\"uLow\":115.25,\"uVolume\":5045475,\"change\":0.66,\"changePercent\":0.5663,\"label\":\"Dec 31, 18\",\"changeOverTime\":-0.08024},{\"date\":\"2019-01-02\",\"open\":116.68,\"close\":119.79,\"high\":117.23,\"low\":116.13,\"volume\":4359779,\"uOpen\":112.79,\"uClose\":120.3,\"uHigh\":116.47,\"uLow\":116.73,\"uVolume\":4289216,\"change\":1.57,\"changePercent\":1.3809,\"label\":\"Jan 2, 19\",\"changeOverTime\":-0.068775},{\"date\":\"2019-01-03\",\"open\":117.8,\"close\":117.2,\"high\":118.22,\"low\":115.85,\"volume\":4478384,\"uOpen\":119.11,\"uClose\":115.78,\"uHigh\":117.22,\"uLow\":114.73,\"uVolume\":4451102,\"change\":-2.4,\"changePercent\":-2.0444,\"label\":\"Jan 3, 19\",\"changeOverTime\":-0.088115},{\"date\":\"2019-01-04\",\"open\":119.46,\"close\":121.9,\"high\":119.56,\"low\":118.68,\"volume\":4576158,\"uOpen\":118.13,\"uClose\":121.25,\"uHigh\":121.24,\"uLow\":119.26,\"uVolume\":4494454,\"change\":4.56,\"changePercent\":4.072,\"label\":\"Jan 4, 19\",\"changeOverTime\":-0.051165},{\"date\":\"2019-01-07\",\"open\":122.7,\"close\":120.4,\"high\":123.13,\"low\":117.05,\"volume\":3901572,\"uOpen\":122.4,\"uClose\":120.76,\"uHigh\":121.53,\"uLow\":118.01,\"uVolume\":3934274,\"change\":0.83,\"changePercent\":0.7148,\"label\":\"Jan 7, 19\",\"changeOverTime\":-0.04455},{\"date\":\"2019-01-08\",\"open\":121.3,\"close\":120.69,\"high\":124.81,\"low\":120.87,\"volume\":4863973,\"uOpen\":125.46,\"uClose\":123.14,\"uHigh\":122.89,\"uLow\":121.54,\"uVolume\":4934477,\"change\":1.7,\"changePercent\":1.4435,\"label\":\"Jan 8, 19\",\"changeOverTime\":-0.031383},{\"date\":\"2019-01-09\",\"open\":122.6,\"close\":124.42,\"high\":126.5,\"low\":124.54,\"volume\":3671366,\"uOpen\":125.45,\"uClose\":121.76,\"uHigh\":126.6,\"uLow\":125.37,\"uVolume\":3698344,\"change\":0.9,\"changePercent\":0.7396,\"label\":\"Jan 9, 19\",\"changeOverTime\":-0.023125},{\"date\":\"2019-01-10\",\"open\":126,\"close\":127.38,\"high\":122.38,\"low\":124.25,\"volume\":3927906,\"uOpen\":123.57,\"uClose\":122.74,\"uHigh\":126.29,\"uLow\":123.66,\"uVolume\":4052337,\"change\":1.1,\"changePercent\":0.9353,\"label\":\"Jan 10, 19\",\"changeOverTime\":-0.014692},{\"date\":\"2019-01-11\",\"open\":127.43,\"close\":127.2,\"high\":123.66,\"low\":122.5,\"volume\":3736113,\"uOpen\":126.46,\"uClose\":122.44,\"uHigh\":123.08,\"uLow\":125.6,\"uVolume\":3852229,\"change\":-0.35,\"changePercent\":-0.273,\"label\":\"Jan 11, 19\",\"changeOverTime\":-0.016943},{\"date\":\"2019-01-14\",\"open\":123.69,\"close\":121.73,\"high\":126.64,\"low\":123.67,\"volume\":5267551,\"uOpen\":121.83,\"uClose\":120.51,\"uHigh\":125.44,\"uLow\":119.8,\"uVolume\":5229278,\"change\":-1.12,\"changePercent\":-0.8817,\"label\":\"Jan 14, 19\",\"changeOverTime\":-0.026745},{\"date\":\"2019-01-15\",\"open\":121.66,\"close\":124.89,\"high\":124.22,\"low\":125.71,\"volume\":3578875,\"uOpen\":125.61,\"uClose\":127.15,\"uHigh\":124.62,\"uLow\":125.1,\"uVolume\":3550223,\"change\":1.35,\"changePercent\":1.13,\"label\":\"Jan 15, 19\",\"changeOverTime\":-0.015038},{\"date\":\"2019-01-16\",\"open\":121.59,\"close\":124.02,\"high\":126,\"low\":124.37,\"volume\":4020161,\"uOpen\":121.82,\"uClose\":121.63,\"uHigh\":127,\"uLow\":123.4,\"uVolume\":3990555,\"change\":-0.11,\"changePercent\":-0.0922,\"label\":\"Jan 16, 19\",\"changeOverTime\":-0.016193},{\"date\":\"2019-01-17\",\"open\":123.37,\"close\":125.7,\"high\":124.7,\"low\":120.97,\"volume\":5161080,\"uOpen\":125.97,\"uClose\":122.42,\"uHigh\":125.7,\"uLow\":125.55,\"uVolume\":5208575,\"change\":0.58,\"changePercent\":0.478,\"label\":\"Jan 17, 19\",\"changeOverTime\":-0.011266},{\"date\":\"2019-01-18\",\"open\":125.72,\"close\":127.49,\"high\":127.06,\"low\":126.8,\"volume\":6112069,\"uOpen\":125.89,\"uClose\":127.58,\"uHigh\":125.59,\"uLow\":126.68,\"uVolume\":6247298,\"change\":1.7,\"changePercent\":1.362,\"label\":\"Jan 18, 19\",\"changeOverTime\":0.002308},{\"date\":\"2019-01-22\",\"open\":128.8,\"close\":123.08,\"high\":127.7,\"low\":122.02,\"volume\":10369432,\"uOpen\":129,\"uClose\":125.35,\"uHigh\":128.9,\"uLow\":126.82,\"uVolume\":10404514,\"change\":-1.3,\"changePercent\":-1.0998,\"label\":\"Jan 22, 19\",\"changeOverTime\":-0.008666},{\"date\":\"2019-01-23\",\"open\":137.72,\"close\":137.53,\"high\":141,\"low\":133.66,\"volume\":22419305,\"uOpen\":136.98,\"uClose\":139.44,\"uHigh\":139,\"uLow\":132.05,\"uVolume\":22181350,\"change\":10.65,\"changePercent\":8.7947,\"label\":\"Jan 23, 19\",\"changeOverTime\":0.076402},{\"date\":\"2019-01-24\",\"open\":139.07,\"close\":138.05,\"high\":135.5,\"low\":137.54,\"volume\":6428043,\"uOpen\":136.81,\"uClose\":133.75,\"uHigh\":135.2,\"uLow\":135.24,\"uVolume\":6606317,\"change\":-0.38,\"changePercent\":-0.2797,\"label\":\"Jan 24, 19\",\"changeOverTime\":0.07419},{\"date\":\"2019-01-25\",\"open\":135.72,\"close\":136.85,\"high\":134.7,\"low\":134.44,\"volume\":5871733,\"uOpen\":138.65,\"uClose\":134.76,\"uHigh\":136.26,\"uLow\":135.15,\"uVolume\":5895797,\"change\":1.49,\"changePercent\":1.1156,\"label\":\"Jan 25, 19\",\"changeOverTime\":0.088171},{\"date\":\"2019-01-28\",\"open\":133.7,\"close\":138.98,\"high\":137.1,\"low\":136.25,\"volume\":5613361,\"uOpen\":138.5,\"uClose\":137.84,\"uHigh\":137,\"uLow\":133.74,\"uVolume\":5537830,\"change\":0.3,\"changePercent\":0.2286,\"label\":\"Jan 28, 19\",\"changeOverTime\":0.089544},{\"date\":\"2019-01-29\",\"open\":135.86,\"close\":138.87,\"high\":136.23,\"low\":137.3,\"volume\":5072356,\"uOpen\":134.79,\"uClose\":140.7,\"uHigh\":137.15,\"uLow\":138.9,\"uVolume\":5252066,\"change\":0.06,\"changePercent\":0.0456,\"label\":\"Jan 29, 19\",\"changeOverTime\":0.09127},{\"date\":\"2019-01-30\",\"open\":137,\"close\":135.35,\"high\":137.66,\"low\":137.41,\"volume\":4659408,\"uOpen\":135,\"uClose\":140.97,\"uHigh\":139.08,\"uLow\":136.22,\"uVolume\":4695447,\"change\":0.05,\"changePercent\":0.0378,\"label\":\"Jan 30, 19\",\"changeOverTime\":0.089863},{\"date\":\"2019-01-31\",\"open\":135.05,\"close\":135.88,\"high\":141.04,\"low\":136.64,\"volume\":4949683,\"uOpen\":137.22,\"uClose\":136.53,\"uHigh\":137.09,\"uLow\":139.91,\"uVolume\":4980041,\"change\":0.04,\"changePercent\":0.0312,\"label\":\"Jan 31, 19\",\"changeOverTime\":0.091015},{\"date\":\"2019-02-01\",\"open\":139.17,\"close\":136.3,\"high\":140.6,\"low\":137.79,\"volume\":3842704,\"uOpen\":138.84,\"uClose\":140.7,\"uHigh\":140.1,\"uLow\":134.8,\"uVolume\":3886891,\"change\":-0.33,\"changePercent\":-0.2483,\"label\":\"Feb 1, 19\",\"changeOverTime\":0.086549},{\"date\":\"2019-02-04\",\"open\":140.1,\"close\":141,\"high\":139,\"low\":137.53,\"volume\":4006465,\"uOpen\":137.51,\"uClose\":141.68,\"uHigh\":139.1,\"uLow\":133.11,\"uVolume\":3990956,\"change\":1.1,\"changePercent\":0.8241,\"label\":\"Feb 4, 19\",\"changeOverTime\":0.095495},{\"date\":\"2019-02-05\",\"open\":140.19,\"close\":138.84,\"high\":140.2,\"low\":136.54,\"volume\":5540229,\"uOpen\":135.53,\"uClose\":139.23,\"uHigh\":140.15,\"uLow\":136.62,\"uVolume\":5568342,\"change\":0.37,\"changePercent\":0.2781,\"label\":\"Feb 5, 19\",\"changeOverTime\":0.101988},{\"date\":\"2019-02-06\",\"open\":141.22,\"close\":141.21,\"high\":142.02,\"low\":141.53,\"volume\":5065222,\"uOpen\":136.27,\"uClose\":138.94,\"uHigh\":142.46,\"uLow\":139.94,\"uVolume\":4945830,\"change\":0.8,\"changePercent\":0.5755,\"label\":\"Feb 6, 19\",\"changeOverTime\":0.104109},{\"date\":\"2019-02-07\",\"open\":136.4,\"close\":133.96,\"high\":140.74,\"low\":137.01,\"volume\":4536932,\"uOpen\":135.44,\"uClose\":135.52,\"uHigh\":141.01,\"uLow\":137.9,\"uVolume\":4440181,\"change\":-3.15,\"changePercent\":-2.3583,\"label\":\"Feb 7, 19\",\"changeOverTime\":0.081129},{\"date\":\"2019-02-08\",\"open\":133.64,\"close\":137.42,\"high\":139.82,\"low\":134.78,\"volume\":3333696,\"uOpen\":134.16,\"uClose\":135.58,\"uHigh\":137.3,\"uLow\":134.93,\"uVolume\":3392795,\"change\":0.52,\"changePercent\":0.3919,\"label\":\"Feb 8, 19\",\"changeOverTime\":0.085064},{\"date\":\"2019-02-11\",\"open\":136.81,\"close\":135.62,\"high\":139.71,\"low\":139.74,\"volume\":3239490,\"uOpen\":136.71,\"uClose\":139.87,\"uHigh\":136.68,\"uLow\":136.14,\"uVolume\":3199868,\"change\":0.29,\"changePercent\":0.2176,\"label\":\"Feb 11, 19\",\"changeOverTime\":0.087351},{\"date\":\"2019-02-12\",\"open\":136.33,\"close\":140.96,\"high\":140.3,\"low\":134.88,\"volume\":3402910,\"uOpen\":137.98,\"uClose\":141.48,\"uHigh\":139.9,\"uLow\":138.72,\"uVolume\":3439856,\"change\":2.1,\"changePercent\":1.5691,\"label\":\"Feb 12, 19\",\"changeOverTime\":0.104862},{\"date\":\"2019-02-13\",\"open\":143.18,\"close\":142.34,\"high\":139.74,\"low\":138.94,\"volume\":4441615,\"uOpen\":143.31,\"uClose\":137.94,\"uHigh\":140.21,\"uLow\":141.26,\"uVolume\":4435789,\"change\":1.48,\"changePercent\":1.1022,\"label\":\"Feb 13, 19\",\"changeOverTime\":0.113361},{\"date\":\"2019-02-14\",\"open\":141.13,\"close\":137.29,\"high\":144.1,\"low\":141.96,\"volume\":2928634,\"uOpen\":143.2,\"uClose\":137.01,\"uHigh\":138.9,\"uLow\":139.2,\"uVolume\":2852697,\"change\":-1.08,\"changePercent\":-0.769,\"label\":\"Feb 14, 19\",\"changeOverTime\":0.105656},{\"date\":\"2019-02-15\",\"open\":141.28,\"close\":142.46,\"high\":140.27,\"low\":142.85,\"volume\":4010961,\"uOpen\":141.02,\"uClose\":144.2,\"uHigh\":142.36,\"uLow\":143.12,\"uVolume\":4028219,\"change\":1.58,\"changePercent\":1.1467,\"label\":\"Feb 15, 19\",\"changeOverTime\":0.12203},{\"date\":\"2019-02-19\",\"open\":139.03,\"close\":140.4,\"high\":144.7,\"low\":137.72,\"volume\":3506714,\"uOpen\":140.55,\"uClose\":141.8,\"uHigh\":144.5,\"uLow\":140.09,\"uVolume\":3402584,\"change\":0.7,\"changePercent\":0.4962,\"label\":\"Feb 19, 19\",\"changeOverTime\":0.127647},{\"date\":\"2019-02-20\",\"open\":145.55,\"close\":140,\"high\":140.72,\"low\":139.23,\"volume\":3885722,\"uOpen\":142.76,\"uClose\":144,\"uHigh\":144.06,\"uLow\":140.86,\"uVolume\":3847067,\"change\":-0.7,\"changePercent\":-0.521,\"label\":\"Feb 20, 19\",\"changeOverTime\":0.121603},{\"date\":\"2019-02-21\",\"open\":140.21,\"close\":140.63,\"high\":139.77,\"low\":138.29,\"volume\":2971752,\"uOpen\":142.7,\"uClose\":138.1,\"uHigh\":142.44,\"uLow\":142.46,\"uVolume\":3062013,\"change\":-0.16,\"changePercent\":-0.1195,\"label\":\"Feb 21, 19\",\"changeOverTime\":0.11719},{\"date\":\"2019-02-22\",\"open\":140.45,\"close\":140.18,\"high\":140.12,\"low\":144.94,\"volume\":3127236,\"uOpen\":143.33,\"uClose\":139.85,\"uHigh\":141.67,\"uLow\":140.34,\"uVolume\":3192236,\"change\":1.44,\"changePercent\":1.0272,\"label\":\"Feb 22, 19\",\"changeOverTime\":0.131062},{\"date\":\"2019-02-25\",\"open\":145,\"close\":140.11,\"high\":142.15,\"low\":143.54,\"volume\":3316799,\"uOpen\":144,\"uClose\":144.85,\"uHigh\":147.3,\"uLow\":142.82,\"uVolume\":3342065,\"change\":0.21,\"changePercent\":0.151,\"label\":\"Feb 25, 19\",\"changeOverTime\":0.132324},{\"date\":\"2019-02-26\",\"open\":142.83,\"close\":141.51,\"high\":145.57,\"low\":140.37,\"volume\":3085206,\"uOpen\":140.7,\"uClose\":139.72,\"uHigh\":142.39,\"uLow\":143.9,\"uVolume\":3147493,\"change\":0.26,\"changePercent\":0.1909,\"label\":\"Feb 26, 19\",\"changeOverTime\":0.1354},{\"date\":\"2019-02-27\",\"open\":142.7,\"close\":144.45,\"high\":146.19,\"low\":140.3,\"volume\":2559105,\"uOpen\":145.85,\"uClose\":140,\"uHigh\":140.12,\"uLow\":144.5,\"uVolume\":2621625,\"change\":-0.56,\"changePercent\":-0.3965,\"label\":\"Feb 27, 19\",\"changeOverTime\":0.126989},{\"date\":\"2019-02-28\",\"open\":139.82,\"close\":143.89,\"high\":139.53,\"low\":144.37,\"volume\":3555184,\"uOpen\":140.8,\"uClose\":144.27,\"uHigh\":145.6,\"uLow\":141.82,\"uVolume\":3475489,\"change\":-1.05,\"changePercent\":-0.7744,\"label\":\"Feb 28, 19\",\"changeOverTime\":0.120895},{\"date\":\"2019-03-01\",\"open\":142.87,\"close\":146.1,\"high\":140.07,\"low\":139.71,\"volume\":3065688,\"uOpen\":145.94,\"uClose\":144.9,\"uHigh\":140.1,\"uLow\":143.27,\"uVolume\":3048552,\"change\":1.12,\"changePercent\":0.8083,\"label\":\"Mar 1, 19\",\"changeOverTime\":0.127621},{\"date\":\"2019-03-04\",\"open\":145.28,\"close\":144.09,\"high\":143.03,\"low\":140.53,\"volume\":3482649,\"uOpen\":142.69,\"uClose\":145.33,\"uHigh\":143.82,\"uLow\":144.01,\"uVolume\":3451176,\"change\":-0.77,\"changePercent\":-0.5749,\"label\":\"Mar 4, 19\",\"changeOverTime\":0.123525},{\"date\":\"2019-03-05\",\"open\":139.16,\"close\":141.03,\"high\":143.68,\"low\":138.46,\"volume\":4345457,\"uOpen\":142.46,\"uClose\":139.28,\"uHigh\":138.89,\"uLow\":138.37,\"uVolume\":4411087,\"change\":-0.56,\"changePercent\":-0.4099,\"label\":\"Mar 5, 19\",\"changeOverTime\":0.121616},{\"date\":\"2019-03-06\",\"open\":144.68,\"close\":138.45,\"high\":139.1,\"low\":138.2,\"volume\":2777288,\"uOpen\":139.33,\"uClose\":143.74,\"uHigh\":144.74,\"uLow\":139.89,\"uVolume\":2886855,\"change\":-0.9,\"changePercent\":-0.6825,\"label\":\"Mar 6, 19\",\"changeOverTime\":0.113608},{\"date\":\"2019-03-07\",\"open\":142.72,\"close\":136.52,\"high\":143.26,\"low\":137.63,\"volume\":4489478,\"uOpen\":138.35,\"uClose\":138.31,\"uHigh\":141.87,\"uLow\":138.14,\"uVolume\":4420693,\"change\":-1.66,\"changePercent\":-1.1827,\"label\":\"Mar 7, 19\",\"changeOverTime\":0.096876},{\"date\":\"2019-03-08\",\"open\":139.73,\"close\":140.28,\"high\":139.85,\"low\":138.42,\"volume\":3815705,\"uOpen\":138.84,\"uClose\":139.09,\"uHigh\":138.77,\"uLow\":133.59,\"uVolume\":3880524,\"change\":-0.28,\"changePercent\":-0.2046,\"label\":\"Mar 8, 19\",\"changeOverTime\":0.097671},{\"date\":\"2019-03-11\",\"open\":142.45,\"close\":142.2,\"high\":139.86,\"low\":136.93,\"volume\":3981618,\"uOpen\":141.68,\"uClose\":142.46,\"uHigh\":141.3,\"uLow\":142.01,\"uVolume\":3977172,\"change\":2.75,\"changePercent\":1.9415,\"label\":\"Mar 11, 19\",\"changeOverTime\":0.1182},{\"date\":\"2019-03-12\",\"open\":144.44,\"close\":139.01,\"high\":141.38,\"low\":143.98,\"volume\":4017336,\"uOpen\":141.69,\"uClose\":140.6,\"uHigh\":139.79,\"uLow\":143.06,\"uVolume\":3996635,\"change\":0.58,\"changePercent\":0.4208,\"label\":\"Mar 12, 19\",\"changeOverTime\":0.122447},{\"date\":\"2019-03-13\",\"open\":138.91,\"close\":144.57,\"high\":142.67,\"low\":140.85,\"volume\":3188362,\"uOpen\":140.66,\"uClose\":139.57,\"uHigh\":139.33,\"uLow\":143.45,\"uVolume\":3243202,\"change\":0.29,\"changePercent\":0.204,\"label\":\"Mar 13, 19\",\"changeOverTime\":0.12561},{\"date\":\"2019-03-14\",\"open\":144.78,\"close\":144.07,\"high\":142.99,\"low\":139.33,\"volume\":2812060,\"uOpen\":145.17,\"uClose\":144.5,\"uHigh\":143.01,\"uLow\":139.2,\"uVolume\":2756631,\"change\":0.24,\"changePercent\":0.169,\"label\":\"Mar 14, 19\",\"changeOverTime\":0.126506},{\"date\":\"2019-03-15\",\"open\":142.98,\"close\":140.54,\"high\":143.22,\"low\":142.99,\"volume\":7304795,\"uOpen\":141.11,\"uClose\":145.82,\"uHigh\":144.66,\"uLow\":144.6,\"uVolume\":7358871,\"change\":0.66,\"changePercent\":0.4722,\"label\":\"Mar 15, 19\",\"changeOverTime\":0.133129},{\"date\":\"2019-03-18\",\"open\":143.61,\"close\":146.34,\"high\":147.09,\"low\":143.96,\"volume\":3383728,\"uOpen\":145.24,\"uClose\":147.1,\"uHigh\":143.58,\"uLow\":145.6,\"uVolume\":3388813,\"change\":0.8,\"changePercent\":0.5717,\"label\":\"Mar 18, 19\",\"changeOverTime\":0.139299},{\"date\":\"2019-03-19\",\"open\":144.33,\"close\":143.21,\"high\":145,\"low\":145,\"volume\":3585279,\"uOpen\":143.87,\"uClose\":142.56,\"uHigh\":144.5,\"uLow\":143,\"uVolume\":3621617,\"change\":0.29,\"changePercent\":0.2027,\"label\":\"Mar 19, 19\",\"changeOverTime\":0.143159},{\"date\":\"2019-03-20\",\"open\":144.65,\"close\":143.8,\"high\":141.3,\"low\":141.14,\"volume\":3812661,\"uOpen\":147.44,\"uClose\":141.6,\"uHigh\":144.7,\"uLow\":141.68,\"uVolume\":3773611,\"change\":-0.9,\"changePercent\":-0.6339,\"label\":\"Mar 20, 19\",\"changeOverTime\":0.131251},{\"date\":\"2019-03-21\",\"open\":139.3,\"close\":141.98,\"high\":143.82,\"low\":142.8,\"volume\":3775683,\"uOpen\":140.8,\"uClose\":146.45,\"uHigh\":142.82,\"uLow\":145.03,\"uVolume\":3743751,\"change\":1.87,\"changePercent\":1.3598,\"label\":\"Mar 21, 19\",\"changeOverTime\":0.14757},{\"date\":\"2019-03-22\",\"open\":141.29,\"close\":140.92,\"high\":145.16,\"low\":142.9,\"volume\":3970266,\"uOpen\":146.09,\"uClose\":139.63,\"uHigh\":143.4,\"uLow\":140.3,\"uVolume\":3977660,\"change\":-2.05,\"changePercent\":-1.419,\"label\":\"Mar 22, 19\",\"changeOverTime\":0.134805},{\"date\":\"2019-03-25\",\"open\":144.79,\"close\":143.19,\"high\":141.13,\"low\":144.36,\"volume\":2956654,\"uOpen\":139.53,\"uClose\":141.97,\"uHigh\":146.67,\"uLow\":143.8,\"uVolume\":2867170,\"change\":-0.28,\"changePercent\":-0.2001,\"label\":\"Mar 25, 19\",\"changeOverTime\":0.128772},{\"date\":\"2019-03-26\",\"open\":140.32,\"close\":145.8,\"high\":145.81,\"low\":142.78,\"volume\":2628053,\"uOpen\":146.19,\"uClose\":146.07,\"uHigh\":143.03,\"uLow\":141.39,\"uVolume\":2573140,\"change\":1.05,\"changePercent\":0.7721,\"label\":\"Mar 26, 19\",\"changeOverTime\":0.135192},{\"date\":\"2019-03-27\",\"open\":141.38,\"close\":142.88,\"high\":141.76,\"low\":142.5,\"volume\":3152734,\"uOpen\":141.97,\"uClose\":141.38,\"uHigh\":144.27,\"uLow\":143.2,\"uVolume\":3124075,\"change\":-1.03,\"changePercent\":-0.7117,\"label\":\"Mar 27, 19\",\"changeOverTime\":0.131421},{\"date\":\"2019-03-28\",\"open\":142.01,\"close\":145.58,\"high\":146.32,\"low\":145.8,\"volume\":2693887,\"uOpen\":145.69,\"uClose\":145.74,\"uHigh\":141.55,\"uLow\":140.4,\"uVolume\":2687597,\"change\":0.71,\"changePercent\":0.4976,\"label\":\"Mar 28, 19\",\"changeOverTime\":0.134442},{\"date\":\"2019-03-29\",\"open\":144.3,\"close\":144.5,\"high\":146.42,\"low\":142.3,\"volume\":3178789,\"uOpen\":143.1,\"uClose\":145.6,\"uHigh\":146.85,\"uLow\":146.53,\"uVolume\":3135963,\"change\":1.22,\"changePercent\":0.8783,\"label\":\"Mar 29, 19\",\"changeOverTime\":0.14646},{\"date\":\"2019-04-01\",\"open\":145.08,\"close\":150.1,\"high\":145.62,\"low\":142.6,\"volume\":4174910,\"uOpen\":142.88,\"uClose\":144.7,\"uHigh\":147.79,\"uLow\":148.23,\"uVolume\":4021256,\"change\":2.3,\"changePercent\":1.5819,\"label\":\"Apr 1, 19\",\"changeOverTime\":0.165867},{\"date\":\"2019-04-02\",\"open\":146.49,\"close\":145,\"high\":147.66,\"low\":149,\"volume\":2456214,\"uOpen\":144.02,\"uClose\":144,\"uHigh\":150.58,\"uLow\":148,\"uVolume\":2444574,\"change\":-0.3,\"changePercent\":-0.215,\"label\":\"Apr 2, 19\",\"changeOverTime\":0.16002},{\"date\":\"2019-04-03\",\"open\":148.97,\"close\":145.19,\"high\":147.7,\"low\":145.67,\"volume\":2806745,\"uOpen\":147.19,\"uClose\":143.68,\"uHigh\":144.85,\"uLow\":149.03,\"uVolume\":2751014,\"change\":0.66,\"changePercent\":0.4468,\"label\":\"Apr 3, 19\",\"changeOverTime\":0.169689},{\"date\":\"2019-04-04\",\"open\":146.94,\"close\":145.49,\"high\":146.8,\"low\":146.83,\"volume\":2881279,\"uOpen\":149.93,\"uClose\":147.72,\"uHigh\":149.07,\"uLow\":143.12,\"uVolume\":2799603,\"change\":-0.85,\"changePercent\":-0.6154,\"label\":\"Apr 4, 19\",\"changeOverTime\":0.162433},{\"date\":\"2019-04-05\",\"open\":146.57,\"close\":146.62,\"high\":146.9,\"low\":145.81,\"volume\":2812584,\"uOpen\":147.1,\"uClose\":146.48,\"uHigh\":148.7,\"uLow\":144.78,\"uVolume\":2860770,\"change\":0.5,\"changePercent\":0.3503,\"label\":\"Apr 5, 19\",\"changeOverTime\":0.16496},{\"date\":\"2019-04-08\",\"open\":148.85,\"close\":143.76,\"high\":148.29,\"low\":145.24,\"volume\":2175351,\"uOpen\":148.49,\"uClose\":150.45,\"uHigh\":146.21,\"uLow\":143.93,\"uVolume\":2208407,\"change\":0.11,\"changePercent\":0.0802,\"label\":\"Apr 8, 19\",\"changeOverTime\":0.164914},{\"date\":\"2019-04-09\",\"open\":149.14,\"close\":146.79,\"high\":146.74,\"low\":143.34,\"volume\":3097559,\"uOpen\":146,\"uClose\":143.44,\"uHigh\":144.28,\"uLow\":143.17,\"uVolume\":3159499,\"change\":-1.32,\"changePercent\":-0.9034,\"label\":\"Apr 9, 19\",\"changeOverTime\":0.153335},{\"date\":\"2019-04-10\",\"open\":147.4,\"close\":149.44,\"high\":150.65,\"low\":144.95,\"volume\":2726529,\"uOpen\":147.4,\"uClose\":144.99,\"uHigh\":150.53,\"uLow\":145.14,\"uVolume\":2748126,\"change\":0.92,\"changePercent\":0.6436,\"label\":\"Apr 10, 19\",\"changeOverTime\":0.1585},{\"date\":\"2019-04-11\",\"open\":144,\"close\":147.38,\"high\":149.04,\"low\":148.49,\"volume\":2952311,\"uOpen\":147.5,\"uClose\":150.24,\"uHigh\":150.4,\"uLow\":145.76,\"uVolume\":3062630,\"change\":0.78,\"changePercent\":0.5413,\"label\":\"Apr 11, 19\",\"changeOverTime\":0.166087},{\"date\":\"2019-04-12\",\"open\":146.19,\"close\":149.81,\"high\":151.43,\"low\":146.1,\"volume\":2900033,\"uOpen\":148.21,\"uClose\":151.53,\"uHigh\":150.93,\"uLow\":146.7,\"uVolume\":2997117,\"change\":0.57,\"changePercent\":0.4115,\"label\":\"Apr 12, 19\",\"changeOverTime\":0.170737},{\"date\":\"2019-04-15\",\"open\":151,\"close\":145.4,\"high\":148.8,\"low\":149.35,\"volume\":3635872,\"uOpen\":147,\"uClose\":148.1,\"uHigh\":145.12,\"uLow\":144.42,\"uVolume\":3701797,\"change\":-0.47,\"changePercent\":-0.3233,\"label\":\"Apr 15, 19\",\"changeOverTime\":0.165872},{\"date\":\"2019-04-16\",\"open\":148.3,\"close\":146.36,\"high\":148.37,\"low\":146.28,\"volume\":6461952,\"uOpen\":149.4,\"uClose\":149.83,\"uHigh\":149.72,\"uLow\":150.34,\"uVolume\":6414904,\"change\":1.3,\"changePercent\":0.8822,\"label\":\"Apr 16, 19\",\"changeOverTime\":0.177481},{\"date\":\"2019-04-17\",\"open\":137.43,\"close\":141.48,\"high\":144.27,\"low\":138.05,\"volume\":13110437,\"uOpen\":138.53,\"uClose\":145.88,\"uHigh\":146.3,\"uLow\":139.76,\"uVolume\":12909904,\"change\":-6.2,\"changePercent\":-4.3278,\"label\":\"Apr 17, 19\",\"changeOverTime\":0.130815},{\"date\":\"2019-04-18\",\"open\":140,\"close\":141.6,\"high\":147.33,\"low\":143.5,\"volume\":5201654,\"uOpen\":141,\"uClose\":146.52,\"uHigh\":141.43,\"uLow\":141.8,\"uVolume\":5039297,\"change\":1.28,\"changePercent\":0.881,\"label\":\"Apr 18, 19\",\"changeOverTime\":0.139428},{\"date\":\"2019-04-22\",\"open\":144.76,\"close\":143.87,\"high\":143.36,\"low\":138.98,\"volume\":4571886,\"uOpen\":142.5,\"uClose\":142.13,\"uHigh\":142.97,\"uLow\":140.22,\"uVolume\":4674711,\"change\":-1.47,\"changePercent\":-1.0461,\"label\":\"Apr 22, 19\",\"changeOverTime\":0.126399},{\"date\":\"2019-04-23\",\"open\":145.41,\"close\":146.68,\"high\":145.85,\"low\":144.97,\"volume\":4594749,\"uOpen\":140.87,\"uClose\":145.57,\"uHigh\":145.44,\"uLow\":140.93,\"uVolume\":4517590,\"change\":1.55,\"changePercent\":1.138,\"label\":\"Apr 23, 19\",\"changeOverTime\":0.143211},{\"date\":\"2019-04-24\",\"open\":143.4,\"close\":140.84,\"high\":144.5,\"low\":141.71,\"volume\":2814052,\"uOpen\":143.8,\"uClose\":141.54,\"uHigh\":144.73,\"uLow\":143.69,\"uVolume\":2901413,\"change\":-0.5,\"changePercent\":-0.3574,\"label\":\"Apr 24, 19\",\"changeOverTime\":0.134377},{\"date\":\"2019-04-25\",\"open\":139.9,\"close\":143.81,\"high\":141.12,\"low\":141.91,\"volume\":2981943,\"uOpen\":141.8,\"uClose\":145.06,\"uHigh\":143.42,\"uLow\":140.01,\"uVolume\":2950170,\"change\":-1.35,\"changePercent\":-0.9463,\"label\":\"Apr 25, 19\",\"changeOverTime\":0.125981},{\"date\":\"2019-04-26\",\"open\":141.42,\"close\":139.47,\"high\":143.39,\"low\":141.49,\"volume\":2410003,\"uOpen\":139.98,\"uClose\":141,\"uHigh\":145.32,\"uLow\":144.72,\"uVolume\":2353753,\"change\":0.83,\"changePercent\":0.5861,\"label\":\"Apr 26, 19\",\"changeOverTime\":0.129009},{\"date\":\"2019-04-29\",\"open\":139.71,\"close\":145.18,\"high\":141.72,\"low\":143.12,\"volume\":3005220,\"uOpen\":139.99,\"uClose\":139.55,\"uHigh\":142.98,\"uLow\":143.11,\"uVolume\":3071589,\"change\":-0.4,\"changePercent\":-0.2814,\"label\":\"Apr 29, 19\",\"changeOverTime\":0.130249},{\"date\":\"2019-04-30\",\"open\":140.51,\"close\":146.86,\"high\":146.05,\"low\":145.28,\"volume\":4706199,\"uOpen\":141.63,\"uClose\":143.95,\"uHigh\":143.56,\"uLow\":141.62,\"uVolume\":4838585,\"change\":1.27,\"changePercent\":0.8982,\"label\":\"Apr 30, 19\",\"changeOverTime\":0.139592},{\"date\":\"2019-05-01\",\"open\":142.27,\"close\":141.49,\"high\":144.41,\"low\":143.3,\"volume\":3094350,\"uOpen\":140.75,\"uClose\":142.41,\"uHigh\":145.57,\"uLow\":144.59,\"uVolume\":3129478,\"change\":0.3,\"changePercent\":0.2071,\"label\":\"May 1, 19\",\"changeOverTime\":0.140157},{\"date\":\"2019-05-02\",\"open\":146.8,\"close\":141.65,\"high\":146.78,\"low\":145.09,\"volume\":3611803,\"uOpen\":141,\"uClose\":142.23,\"uHigh\":147.06,\"uLow\":140.39,\"uVolume\":3710133,\"change\":-0.97,\"changePercent\":-0.7017,\"label\":\"May 2, 19\",\"changeOverTime\":0.132557},{\"date\":\"2019-05-03\",\"open\":146.5,\"close\":141.74,\"high\":142,\"low\":142.23,\"volume\":2618285,\"uOpen\":143.4,\"uClose\":141.19,\"uHigh\":143,\"uLow\":143.43,\"uVolume\":2584430,\"change\":0.68,\"changePercent\":0.4925,\"label\":\"May 3, 19\",\"changeOverTime\":0.14198},{\"date\":\"2019-05-06\",\"open\":140,\"close\":146.44,\"high\":140.79,\"low\":140.3,\"volume\":2821583,\"uOpen\":144.7,\"uClose\":144.82,\"uHigh\":144.38,\"uLow\":144.1,\"uVolume\":2915575,\"change\":0.13,\"changePercent\":0.0963,\"label\":\"May 6, 19\",\"changeOverTime\":0.139549},{\"date\":\"2019-05-07\",\"open\":142.38,\"close\":142.17,\"high\":146.43,\"low\":138.8,\"volume\":4790556,\"uOpen\":140.98,\"uClose\":142.14,\"uHigh\":142.3,\"uLow\":141.37,\"uVolume\":4878372,\"change\":-2.81,\"changePercent\":-2.0174,\"label\":\"May 7, 19\",\"changeOverTime\":0.116392},{\"date\":\"2019-05-08\",\"open\":139.86,\"close\":144,\"high\":145,\"low\":137.81,\"volume\":3846504,\"uOpen\":142.05,\"uClose\":139,\"uHigh\":141.7,\"uLow\":142.16,\"uVolume\":3962547,\"change\":0.37,\"changePercent\":0.2736,\"label\":\"May 8, 19\",\"changeOverTime\":0.117583},{\"date\":\"2019-05-09\",\"open\":135.33,\"close\":138.82,\"high\":141.99,\"low\":134.77,\"volume\":4209859,\"uOpen\":137.6,\"uClose\":136.52,\"uHigh\":137.63,\"uLow\":137.6,\"uVolume\":4243398,\"change\":-2.67,\"changePercent\":-1.9496,\"label\":\"May 9, 19\",\"changeOverTime\":0.097647},{\"date\":\"2019-05-10\",\"open\":137.26,\"close\":141.87,\"high\":137.13,\"low\":137.15,\"volume\":4113793,\"uOpen\":138.37,\"uClose\":138.01,\"uHigh\":140.87,\"uLow\":134.18,\"uVolume\":4057312,\"change\":-0.02,\"changePercent\":-0.015,\"label\":\"May 10, 19\",\"changeOverTime\":0.098581},{\"date\":\"2019-05-13\",\"open\":134.84,\"close\":135.16,\"high\":137.62,\"low\":133.5,\"volume\":5193158,\"uOpen\":138.79,\"uClose\":137.55,\"uHigh\":138.6,\"uLow\":136.9,\"uVolume\":4999834,\"change\":-3.9,\"changePercent\":-2.8864,\"label\":\"May 13, 19\",\"changeOverTime\":0.066904},{\"date\":\"2019-05-14\",\"open\":136.79,\"close\":138.51,\"high\":137.54,\"low\":134.39,\"volume\":3417700,\"uOpen\":133.79,\"uClose\":137.34,\"uHigh\":139.46,\"uLow\":135.95,\"uVolume\":3290567,\"change\":1.97,\"changePercent\":1.5036,\"label\":\"May 14, 19\",\"changeOverTime\":0.079182},{\"date\":\"2019-05-15\",\"open\":137.72,\"close\":138.5,\"high\":138.2,\"low\":134.7,\"volume\":2676808,\"uOpen\":137.22,\"uClose\":140.7,\"uHigh\":139.15,\"uLow\":136.49,\"uVolume\":2665270,\"change\":1.09,\"changePercent\":0.8275,\"label\":\"May 15, 19\",\"changeOverTime\":0.089251},{\"date\":\"2019-05-16\",\"open\":136.08,\"close\":136.45,\"high\":142.26,\"low\":139.17,\"volume\":3116800,\"uOpen\":140.32,\"uClose\":138.31,\"uHigh\":139.31,\"uLow\":138.69,\"uVolume\":3155895,\"change\":1.49,\"changePercent\":1.1412,\"label\":\"May 16, 19\",\"changeOverTime\":0.102558},{\"date\":\"2019-05-17\",\"open\":134.96,\"close\":135.44,\"high\":136.8,\"low\":135.07,\"volume\":2692396,\"uOpen\":140.08,\"uClose\":138.9,\"uHigh\":141.73,\"uLow\":134.44,\"uVolume\":2682696,\"change\":-1.62,\"changePercent\":-1.2021,\"label\":\"May 17, 19\",\"changeOverTime\":0.091401},{\"date\":\"2019-05-20\",\"open\":135.48,\"close\":141.02,\"high\":137.67,\"low\":135.1,\"volume\":3333295,\"uOpen\":135.34,\"uClose\":138.55,\"uHigh\":138.71,\"uLow\":133.23,\"uVolume\":3297022,\"change\":0.8,\"changePercent\":0.6181,\"label\":\"May 20, 19\",\"changeOverTime\":0.095984},{\"date\":\"2019-05-21\",\"open\":141.54,\"close\":142.36,\"high\":137.54,\"low\":142.54,\"volume\":2617699,\"uOpen\":140.6,\"uClose\":139.72,\"uHigh\":140.6,\"uLow\":139.88,\"uVolume\":2504464,\"change\":1.38,\"changePercent\":1.0116,\"label\":\"May 21, 19\",\"changeOverTime\":0.108523},{\"date\":\"2019-05-22\",\"open\":138,\"close\":139.18,\"high\":141.79,\"low\":140.84,\"volume\":1886179,\"uOpen\":139,\"uClose\":142.91,\"uHigh\":137.65,\"uLow\":138.76,\"uVolume\":1936762,\"change\":-0.1,\"changePercent\":-0.0752,\"label\":\"May 22, 19\",\"changeOverTime\":0.106877},{\"date\":\"2019-05-23\",\"open\":135.81,\"close\":135.73,\"high\":140.03,\"low\":132.6,\"volume\":5556601,\"uOpen\":136.19,\"uClose\":134.33,\"uHigh\":136.1,\"uLow\":131.17,\"uVolume\":5496182,\"change\":-4.01,\"changePercent\":-2.9912,\"label\":\"May 23, 19\",\"changeOverTime\":0.073979},{\"date\":\"2019-05-24\",\"open\":134.36,\"close\":134.74,\"high\":134.6,\"low\":136.06,\"volume\":2636903,\"uOpen\":135.09,\"uClose\":135,\"uHigh\":136.08,\"uLow\":134.14,\"uVolume\":2695879,\"change\":-0.11,\"changePercent\":-0.0852,\"label\":\"May 24, 19\",\"changeOverTime\":0.072717},{\"date\":\"2019-05-28\",\"open\":138.19,\"close\":131.19,\"high\":138.79,\"low\":130.64,\"volume\":4946575,\"uOpen\":134.14,\"uClose\":132.03,\"uHigh\":137.55,\"uLow\":136.68,\"uVolume\":5104204,\"change\":-1.88,\"changePercent\":-1.3923,\"label\":\"May 28, 19\",\"changeOverTime\":0.056739},{\"date\":\"2019-05-29\",\"open\":132,\"close\":129.73,\"high\":135.64,\"low\":129.51,\"volume\":3851567,\"uOpen\":134,\"uClose\":132.83,\"uHigh\":135.82,\"uLow\":132.67,\"uVolume\":3835948,\"change\":-0.79,\"changePercent\":-0.6117,\"label\":\"May 29, 19\",\"changeOverTime\":0.050757},{\"date\":\"2019-05-30\",\"open\":129.78,\"close\":130.64,\"high\":132.29,\"low\":131.97,\"volume\":2758956,\"uOpen\":130.93,\"uClose\":134.18,\"uHigh\":130.69,\"uLow\":130.94,\"uVolume\":2779175,\"change\":-0.12,\"changePercent\":-0.0935,\"label\":\"May 30, 19\",\"changeOverTime\":0.05051},{\"date\":\"2019-05-31\",\"open\":131.27,\"close\":128.39,\"high\":129.11,\"low\":128.99,\"volume\":3701658,\"uOpen\":129.22,\"uClose\":127.36,\"uHigh\":134.85,\"uLow\":131.27,\"uVolume\":3540485,\"change\":-2.65,\"changePercent\":-2.0676,\"label\":\"May 31, 19\",\"changeOverTime\":0.028509},{\"date\":\"2019-06-03\",\"open\":130.6,\"close\":132.05,\"high\":133.21,\"low\":127.36,\"volume\":4286352,\"uOpen\":129.9,\"uClose\":130.61,\"uHigh\":132.36,\"uLow\":133.23,\"uVolume\":4429939,\"change\":1.33,\"changePercent\":1.012,\"label\":\"Jun 3, 19\",\"changeOverTime\":0.039221},{\"date\":\"2019-06-04\",\"open\":135.52,\"close\":135.08,\"high\":134.86,\"low\":131.11,\"volume\":3958538,\"uOpen\":133.47,\"uClose\":136.62,\"uHigh\":132.96,\"uLow\":132.08,\"uVolume\":3950111,\"change\":4.5,\"changePercent\":3.5714,\"label\":\"Jun 4, 19\",\"changeOverTime\":0.075337},{\"date\":\"2019-06-05\",\"open\":138.02,\"close\":136.92,\"high\":139.64,\"low\":131.64,\"volume\":3242042,\"uOpen\":137.2,\"uClose\":134.88,\"uHigh\":135.71,\"uLow\":130.58,\"uVolume\":3269090,\"change\":-1.2,\"changePercent\":-0.9051,\"label\":\"Jun 5, 19\",\"changeOverTime\":0.067127},{\"date\":\"2019-06-06\",\"open\":138.56,\"close\":134.13,\"high\":139.2,\"low\":137.2,\"volume\":2390061,\"uOpen\":138.79,\"uClose\":134.94,\"uHigh\":138.74,\"uLow\":134,\"uVolume\":2390170,\"change\":0.76,\"changePercent\":0.5652,\"label\":\"Jun 6, 19\",\"changeOverTime\":0.07039},{\"date\":\"2019-06-07\",\"open\":136.86,\"close\":139.4,\"high\":135.54,\"low\":136.92,\"volume\":2582629,\"uOpen\":133.14,\"uClose\":137.38,\"uHigh\":141.34,\"uLow\":134.55,\"uVolume\":2637574,\"change\":1.1,\"changePercent\":0.8341,\"label\":\"Jun 7, 19\",\"changeOverTime\":0.082501},{\"date\":\"2019-06-10\",\"open\":140.98,\"close\":137.04,\"high\":139.94,\"low\":139.8,\"volume\":2932225,\"uOpen\":140.14,\"uClose\":140.62,\"uHigh\":137.69,\"uLow\":140.23,\"uVolume\":3006241,\"change\":1.44,\"changePercent\":1.0872,\"label\":\"Jun 10, 19\",\"changeOverTime\":0.094321},{\"date\":\"2019-06-11\",\"open\":139.66,\"close\":136.86,\"high\":139.38,\"low\":138.52,\"volume\":3462844,\"uOpen\":139.62,\"uClose\":136.86,\"uHigh\":141.19,\"uLow\":137.88,\"uVolume\":3537773,\"change\":1.22,\"changePercent\":0.911,\"label\":\"Jun 11, 19\",\"changeOverTime\":0.101961},{\"date\":\"2019-06-12\",\"open\":139.7,\"close\":136.95,\"high\":142.73,\"low\":135.26,\"volume\":2346286,\"uOpen\":141.89,\"uClose\":140.99,\"uHigh\":136.08,\"uLow\":139.92,\"uVolume\":2257432,\"change\":-1.1,\"changePercent\":-0.8171,\"label\":\"Jun 12, 19\",\"changeOverTime\":0.095076},{\"date\":\"2019-06-13\",\"open\":140.2,\"close\":137.11,\"high\":139.4,\"low\":141.41,\"volume\":2986333,\"uOpen\":138,\"uClose\":138.95,\"uHigh\":136.49,\"uLow\":140.57,\"uVolume\":3022569,\"change\":0.93,\"changePercent\":0.683,\"label\":\"Jun 13, 19\",\"changeOverTime\":0.102098},{\"date\":\"2019-06-14\",\"open\":138.85,\"close\":140,\"high\":140.54,\"low\":140.07,\"volume\":2271726,\"uOpen\":137.23,\"uClose\":138.99,\"uHigh\":139.34,\"uLow\":137.74,\"uVolume\":2223791,\"change\":-0.63,\"changePercent\":-0.4713,\"label\":\"Jun 14, 19\",\"changeOverTime\":0.094282},{\"date\":\"2019-06-17\",\"open\":138.44,\"close\":141.57,\"high\":140.51,\"low\":138.7,\"volume\":2288340,\"uOpen\":138.18,\"uClose\":138.37,\"uHigh\":141.29,\"uLow\":139.5,\"uVolume\":2247556,\"change\":-0.2,\"changePercent\":-0.155,\"label\":\"Jun 17, 19\",\"changeOverTime\":0.096193},{\"date\":\"2019-06-18\",\"open\":137.9,\"close\":140.05,\"high\":143.7,\"low\":137.9,\"volume\":2937604,\"uOpen\":140.6,\"uClose\":139.99,\"uHigh\":137.82,\"uLow\":139.8,\"uVolume\":3051593,\"change\":1.43,\"changePercent\":1.0889,\"label\":\"Jun 18, 19\",\"changeOverTime\":0.105567},{\"date\":\"2019-06-19\",\"open\":138.19,\"close\":139.92,\"high\":144.13,\"low\":137.29,\"volume\":2486201,\"uOpen\":140.7,\"uClose\":143.49,\"uHigh\":138.79,\"uLow\":140.69,\"uVolume\":2401337,\"change\":0.7,\"changePercent\":0.5323,\"label\":\"Jun 19, 19\",\"changeOverTime\":0.1133},{\"date\":\"2019-06-20\",\"open\":143.14,\"close\":140.22,\"high\":140.83,\"low\":145,\"volume\":3149074,\"uOpen\":143.74,\"uClose\":143.93,\"uHigh\":143.57,\"uLow\":139.5,\"uVolume\":3217147,\"change\":1.81,\"changePercent\":1.3086,\"label\":\"Jun 20, 19\",\"changeOverTime\":0.125203},{\"date\":\"2019-06-21\",\"open\":142.4,\"close\":145.9,\"high\":146.14,\"low\":142.47,\"volume\":5305850,\"uOpen\":139.71,\"uClose\":145.7,\"uHigh\":143.11,\"uLow\":145.01,\"uVolume\":5307157,\"change\":0.36,\"changePercent\":0.2559,\"label\":\"Jun 21, 19\",\"changeOverTime\":0.127193},{\"date\":\"2019-06-24\",\"open\":142.3,\"close\":141.9,\"high\":141.3,\"low\":144.9,\"volume\":2209716,\"uOpen\":143.9,\"uClose\":142.14,\"uHigh\":141.13,\"uLow\":143.46,\"uVolume\":2218705,\"change\":0.16,\"changePercent\":0.1099,\"label\":\"Jun 24, 19\",\"changeOverTime\":0.131683},{\"date\":\"2019-06-25\",\"open\":146.31,\"close\":140.71,\"high\":140.86,\"low\":144.53,\"volume\":2900093,\"uOpen\":142.73,\"uClose\":144.13,\"uHigh\":142.57,\"uLow\":140.31,\"uVolume\":2909077,\"change\":-1.03,\"changePercent\":-0.7231,\"label\":\"Jun 25, 19\",\"changeOverTime\":0.122406},{\"date\":\"2019-06-26\",\"open\":143.71,\"close\":144.08,\"high\":144.24,\"low\":139.2,\"volume\":2369618,\"uOpen\":138.89,\"uClose\":139.17,\"uHigh\":141.44,\"uLow\":141,\"uVolume\":2293154,\"change\":0.17,\"changePercent\":0.1166,\"label\":\"Jun 26, 19\",\"changeOverTime\":0.125646},{\"date\":\"2019-06-27\",\"open\":141.28,\"close\":143.53,\"high\":141.5,\"low\":144.32,\"volume\":2172908,\"uOpen\":140.93,\"uClose\":144.58,\"uHigh\":140.5,\"uLow\":142.35,\"uVolume\":2163946,\"change\":0,\"changePercent\":0,\"label\":\"Jun 27, 19\",\"changeOverTime\":0.123153},{\"date\":\"2019-06-28\",\"open\":138.96,\"close\":138.5,\"high\":140.72,\"low\":143.66,\"volume\":6456478,\"uOpen\":143.59,\"uClose\":142.8,\"uHigh\":142.06,\"uLow\":142.78,\"uVolume\":6325918,\"change\":-0.62,\"changePercent\":-0.4697,\"label\":\"Jun 28, 19\",\"changeOverTime\":0.11701},{\"date\":\"2019-07-01\",\"open\":144,\"close\":143.12,\"high\":141.93,\"low\":140.62,\"volume\":3441819,\"uOpen\":143.6,\"uClose\":146.11,\"uHigh\":142.75,\"uLow\":144.02,\"uVolume\":3491552,\"change\":2.03,\"changePercent\":1.4407,\"label\":\"Jul 1, 19\",\"changeOverTime\":0.137893},{\"date\":\"2019-07-02\",\"open\":146.18,\"close\":141.76,\"high\":140.75,\"low\":146.43,\"volume\":2426423,\"uOpen\":143.64,\"uClose\":141.9,\"uHigh\":141.56,\"uLow\":141.6,\"uVolume\":2529874,\"change\":0.36,\"changePercent\":0.2486,\"label\":\"Jul 2, 19\",\"changeOverTime\":0.139733},{\"date\":\"2019-07-03\",\"open\":146.6,\"close\":147.03,\"high\":147.09,\"low\":142.06,\"volume\":2013524,\"uOpen\":143.56,\"uClose\":146.94,\"uHigh\":148.78,\"uLow\":142.41,\"uVolume\":1991238,\"change\":1.34,\"changePercent\":0.9475,\"label\":\"Jul 3, 19\",\"changeOverTime\":0.151097},{\"date\":\"2019-07-05\",\"open\":143.89,\"close\":142.19,\"high\":142.1,\"low\":146.74,\"volume\":2230407,\"uOpen\":146.29,\"uClose\":145.43,\"uHigh\":144.29,\"uLow\":146.48,\"uVolume\":2146897,\"change\":-0.16,\"changePercent\":-0.118,\"label\":\"Jul 5, 19\",\"changeOverTime\":0.149644},{\"date\":\"2019-07-08\",\"open\":143,\"close\":147.01,\"high\":142.41,\"low\":142.84,\"volume\":2505534,\"uOpen\":142.66,\"uClose\":142.38,\"uHigh\":147.84,\"uLow\":143.16,\"uVolume\":2441063,\"change\":-0.84,\"changePercent\":-0.5826,\"label\":\"Jul 8, 19\",\"changeOverTime\":0.14256},{\"date\":\"2019-07-09\",\"open\":143.41,\"close\":141.07,\"high\":147.7,\"low\":144.76,\"volume\":3425361,\"uOpen\":146.14,\"uClose\":142.84,\"uHigh\":146.21,\"uLow\":140.81,\"uVolume\":3502307,\"change\":-1.27,\"changePercent\":-0.9131,\"label\":\"Jul 9, 19\",\"changeOverTime\":0.129444},{\"date\":\"2019-07-10\",\"open\":145,\"close\":145.01,\"high\":142.08,\"low\":144.16,\"volume\":2571240,\"uOpen\":144,\"uClose\":145.14,\"uHigh\":146.52,\"uLow\":143.53,\"uVolume\":2569148,\"change\":1.16,\"changePercent\":0.8521,\"label\":\"Jul 10, 19\",\"changeOverTime\":0.143634},{\"date\":\"2019-07-11\",\"open\":143.32,\"close\":145.93,\"high\":146.85,\"low\":140.9,\"volume\":2169206,\"uOpen\":145.52,\"uClose\":147.32,\"uHigh\":147.25,\"uLow\":145,\"uVolume\":2194962,\"change\":0.78,\"changePercent\":0.5487,\"label\":\"Jul 11, 19\",\"changeOverTime\":0.148854},{\"date\":\"2019-07-12\",\"open\":144.36,\"close\":144.47,\"high\":144.7,\"low\":142.98,\"volume\":2812007,\"uOpen\":142.51,\"uClose\":147.89,\"uHigh\":145.72,\"uLow\":142.52,\"uVolume\":2800820,\"change\":1.5,\"changePercent\":1.111,\"label\":\"Jul 12, 19\",\"changeOverTime\":0.158172},{\"date\":\"2019-07-15\",\"open\":148.64,\"close\":147.14,\"high\":149.46,\"low\":145.86,\"volume\":2440139,\"uOpen\":149.21,\"uClose\":147.91,\"uHigh\":145.44,\"uLow\":146.37,\"uVolume\":2465262,\"change\":0.59,\"changePercent\":0.4193,\"label\":\"Jul 15, 19\",\"changeOverTime\":0.16493},{\"date\":\"2019-07-16\",\"open\":143.82,\"close\":148.13,\"high\":144.19,\"low\":144.36,\"volume\":3509865,\"uOpen\":144.75,\"uClose\":143.68,\"uHigh\":150.5,\"uLow\":147.64,\"uVolume\":3551184,\"change\":0.21,\"changePercent\":0.1521,\"label\":\"Jul 16, 19\",\"changeOverTime\":0.16596},{\"date\":\"2019-07-17\",\"open\":147.12,\"close\":143.2,\"high\":150.8,\"low\":145.05,\"volume\":6144550,\"uOpen\":150.21,\"uClose\":147.75,\"uHigh\":144.9,\"uLow\":146.59,\"uVolume\":6236121,\"change\":-0.47,\"changePercent\":-0.3323,\"label\":\"Jul 17, 19\",\"changeOverTime\":0.165151},{\"date\":\"2019-07-18\",\"open\":146.8,\"close\":149.99,\"high\":150.62,\"low\":147.5,\"volume\":12743061,\"uOpen\":148.6,\"uClose\":154.14,\"uHigh\":151.62,\"uLow\":148.3,\"uVolume\":13284054,\"change\":6.6,\"changePercent\":4.7195,\"label\":\"Jul 18, 19\",\"changeOverTime\":0.213651},{\"date\":\"2019-07-19\",\"open\":152.14,\"close\":152.63,\"high\":158.27,\"low\":150.44,\"volume\":7406193,\"uOpen\":154.46,\"uClose\":150.62,\"uHigh\":158.19,\"uLow\":154.92,\"uVolume\":7590040,\"change\":0.05,\"changePercent\":0.0344,\"label\":\"Jul 19, 19\",\"changeOverTime\":0.212966},{\"date\":\"2019-07-22\",\"open\":154.6,\"close\":155.92,\"high\":159.23,\"low\":156.8,\"volume\":4249226,\"uOpen\":150.75,\"uClose\":156.33,\"uHigh\":154.38,\"uLow\":154.4,\"uVolume\":4204959,\"change\":0.06,\"changePercent\":0.041,\"label\":\"Jul 22, 19\",\"changeOverTime\":0.217045},{\"date\":\"2019-07-23\",\"open\":155.49,\"close\":157.58,\"high\":156.29,\"low\":149.23,\"volume\":3921464,\"uOpen\":151.96,\"uClose\":152.59,\"uHigh\":150.98,\"uLow\":155.4,\"uVolume\":3769699,\"change\":0.65,\"changePercent\":0.4475,\"label\":\"Jul 23, 19\",\"changeOverTime\":0.217508},{\"date\":\"2019-07-24\",\"open\":153.43,\"close\":152.97,\"high\":156.77,\"low\":153.89,\"volume\":3070253,\"uOpen\":152.27,\"uClose\":150.94,\"uHigh\":152.45,\"uLow\":149.77,\"uVolume\":3074272,\"change\":-0.37,\"changePercent\":-0.2343,\"label\":\"Jul 24, 19\",\"changeOverTime\":0.222795},{\"date\":\"2019-07-25\",\"open\":155.49,\"close\":155.07,\"high\":155.79,\"low\":155.16,\"volume\":3123382,\"uOpen\":152.91,\"uClose\":151.32,\"uHigh\":151.58,\"uLow\":149.44,\"uVolume\":3206739,\"change\":0.38,\"changePercent\":0.24,\"label\":\"Jul 25, 19\",\"changeOverTime\":0.218432},{\"date\":\"2019-07-26\",\"open\":157,\"close\":152.07,\"high\":156.11,\"low\":152.3,\"volume\":3022894,\"uOpen\":155,\"uClose\":158.56,\"uHigh\":157.95,\"uLow\":153,\"uVolume\":2962257,\"change\":1,\"changePercent\":0.668,\"label\":\"Jul 26, 19\",\"changeOverTime\":0.23639},{\"date\":\"2019-07-29\",\"open\":158.39,\"close\":155.24,\"high\":152.57,\"low\":152.2,\"volume\":2145345,\"uOpen\":152.68,\"uClose\":153.78,\"uHigh\":154.14,\"uLow\":153.35,\"uVolume\":2178651,\"change\":-0.5,\"changePercent\":-0.3198,\"label\":\"Jul 29, 19\",\"changeOverTime\":0.22583},{\"date\":\"2019-07-30\",\"open\":156,\"close\":155.56,\"high\":150.9,\"low\":151.89,\"volume\":2726191,\"uOpen\":155,\"uClose\":152.49,\"uHigh\":154.7,\"uLow\":154.57,\"uVolume\":2716649,\"change\":-1.14,\"changePercent\":-0.7478,\"label\":\"Jul 30, 19\",\"changeOverTime\":0.21872},{\"date\":\"2019-07-31\",\"open\":154.4,\"close\":152.87,\"high\":157.17,\"low\":148.79,\"volume\":3731797,\"uOpen\":155.4,\"uClose\":149.78,\"uHigh\":153.87,\"uLow\":151.12,\"uVolume\":3900646,\"change\":-1.55,\"changePercent\":-1.0702,\"label\":\"Jul 31, 19\",\"changeOverTime\":0.199948},{\"date\":\"2019-08-01\",\"open\":155.6,\"close\":155.08,\"high\":156.21,\"low\":151.9,\"volume\":6612675,\"uOpen\":153.3,\"uClose\":152.72,\"uHigh\":158.62,\"uLow\":155.5,\"uVolume\":6384427,\"change\":2.1,\"changePercent\":1.4729,\"label\":\"Aug 1, 19\",\"changeOverTime\":0.217554},{\"date\":\"2019-08-02\",\"open\":149.63,\"close\":151.9,\"high\":153.11,\"low\":149.44,\"volume\":8414021,\"uOpen\":151.42,\"uClose\":148.53,\"uHigh\":158.2,\"uLow\":145.92,\"uVolume\":8188885,\"change\":-3.14,\"changePercent\":-2.0548,\"label\":\"Aug 2, 19\",\"changeOverTime\":0.192102},{\"date\":\"2019-08-05\",\"open\":148.13,\"close\":142.2,\"high\":148.9,\"low\":145.84,\"volume\":7393130,\"uOpen\":150.64,\"uClose\":145.87,\"uHigh\":148.2,\"uLow\":143.71,\"uVolume\":7257749,\"change\":-6.5,\"changePercent\":-4.5993,\"label\":\"Aug 5, 19\",\"changeOverTime\":0.143967},{\"date\":\"2019-08-06\",\"open\":145.63,\"close\":146.44,\"high\":144.49,\"low\":140.01,\"volume\":5140088,\"uOpen\":147.27,\"uClose\":144.54,\"uHigh\":146.84,\"uLow\":143.09,\"uVolume\":5174476,\"change\":-0.03,\"changePercent\":-0.0223,\"label\":\"Aug 6, 19\",\"changeOverTime\":0.144911},{\"date\":\"2019-08-07\",\"open\":142.43,\"close\":140.16,\"high\":140.6,\"low\":136.9,\"volume\":6192257,\"uOpen\":141.12,\"uClose\":139.22,\"uHigh\":145.28,\"uLow\":136.43,\"uVolume\":6052824,\"change\":-1.63,\"changePercent\":-1.2009,\"label\":\"Aug 7, 19\",\"changeOverTime\":0.128392},{\"date\":\"2019-08-08\",\"open\":144.4,\"close\":147.1,\"high\":144.62,\"low\":141.89,\"volume\":5470614,\"uOpen\":142.72,\"uClose\":140.1,\"uHigh\":142.01,\"uLow\":144.29,\"uVolume\":5491426,\"change\":1.03,\"changePercent\":0.7351,\"label\":\"Aug 8, 19\",\"changeOverTime\":0.137672},{\"date\":\"2019-08-09\",\"open\":145.53,\"close\":139.99,\"high\":142.41,\"low\":136.4,\"volume\":5305612,\"uOpen\":140.22,\"uClose\":141.68,\"uHigh\":142.71,\"uLow\":136.9,\"uVolume\":5310164,\"change\":-4.13,\"changePercent\":-2.9366,\"label\":\"Aug 9, 19\",\"changeOverTime\":0.10562},{\"date\":\"2019-08-12\",\"open\":135.81,\"close\":135.27,\"high\":136.41,\"low\":135.32,\"volume\":4208227,\"uOpen\":139.58,\"uClose\":138.46,\"uHigh\":140.12,\"uLow\":134.49,\"uVolume\":4114117,\"change\":-2.07,\"changePercent\":-1.5032,\"label\":\"Aug 12, 19\",\"changeOverTime\":0.08845},{\"date\":\"2019-08-13\",\"open\":134.3,\"close\":137.26,\"high\":139.74,\"low\":138.1,\"volume\":4648694,\"uOpen\":135.8,\"uClose\":142.38,\"uHigh\":137.4,\"uLow\":134.8,\"uVolume\":4658489,\"change\":1.73,\"changePercent\":1.252,\"label\":\"Aug 13, 19\",\"changeOverTime\":0.102133},{\"date\":\"2019-08-14\",\"open\":138.34,\"close\":131.3,\"high\":138.96,\"low\":133.35,\"volume\":5310507,\"uOpen\":138.31,\"uClose\":135,\"uHigh\":136.71,\"uLow\":132.85,\"uVolume\":5137050,\"change\":-4.76,\"changePercent\":-3.4665,\"label\":\"Aug 14, 19\",\"changeOverTime\":0.065404},{\"date\":\"2019-08-15\",\"open\":137.01,\"close\":135.6,\"high\":133.89,\"low\":132.93,\"volume\":3858116,\"uOpen\":137.16,\"uClose\":134.51,\"uHigh\":136.31,\"uLow\":136.58,\"uVolume\":3749529,\"change\":0.69,\"changePercent\":0.5341,\"label\":\"Aug 15, 19\",\"changeOverTime\":0.0707},{\"date\":\"2019-08-16\",\"open\":133.35,\"close\":134.94,\"high\":134.94,\"low\":133.79,\"volume\":3135250,\"uOpen\":137.54,\"uClose\":136.59,\"uHigh\":141.23,\"uLow\":135.26,\"uVolume\":3149483,\"change\":1.9,\"changePercent\":1.4261,\"label\":\"Aug 16, 19\",\"changeOverTime\":0.086552},{\"date\":\"2019-08-19\",\"open\":137.04,\"close\":141.75,\"high\":139.79,\"low\":134.94,\"volume\":3244408,\"uOpen\":136.1,\"uClose\":136.11,\"uHigh\":141.89,\"uLow\":138.68,\"uVolume\":3290500,\"change\":1.29,\"changePercent\":0.9937,\"label\":\"Aug 19, 19\",\"changeOverTime\":0.096382},{\"date\":\"2019-08-20\",\"open\":137.11,\"close\":139,\"high\":136.71,\"low\":138.38,\"volume\":3091814,\"uOpen\":136.88,\"uClose\":139,\"uHigh\":139.82,\"uLow\":134.89,\"uVolume\":3064204,\"change\":-2.06,\"changePercent\":-1.548,\"label\":\"Aug 20, 19\",\"changeOverTime\":0.077051},{\"date\":\"2019-08-21\",\"open\":141.25,\"close\":140.04,\"high\":137.94,\"low\":137.9,\"volume\":2880445,\"uOpen\":140.04,\"uClose\":134.98,\"uHigh\":136.98,\"uLow\":140,\"uVolume\":2863116,\"change\":1.31,\"changePercent\":0.9823,\"label\":\"Aug 21, 19\",\"changeOverTime\":0.088802},{\"date\":\"2019-08-22\",\"open\":135.15,\"close\":139.9,\"high\":136.43,\"low\":136.04,\"volume\":2812908,\"uOpen\":140.1,\"uClose\":140.54,\"uHigh\":141.3,\"uLow\":138.62,\"uVolume\":2813913,\"change\":0.07,\"changePercent\":0.0528,\"label\":\"Aug 22, 19\",\"changeOverTime\":0.090802},{\"date\":\"2019-08-23\",\"open\":138.03,\"close\":129.58,\"high\":136.06,\"low\":131.99,\"volume\":4911689,\"uOpen\":137.32,\"uClose\":133.46,\"uHigh\":137.36,\"uLow\":133.9,\"uVolume\":5054673,\"change\":-4.83,\"changePercent\":-3.703,\"label\":\"Aug 23, 19\",\"changeOverTime\":0.04926},{\"date\":\"2019-08-26\",\"open\":131.2,\"close\":130.56,\"high\":136.4,\"low\":130.14,\"volume\":2968401,\"uOpen\":134.17,\"uClose\":133.51,\"uHigh\":134.9,\"uLow\":130.36,\"uVolume\":2976355,\"change\":0.43,\"changePercent\":0.3309,\"label\":\"Aug 26, 19\",\"changeOverTime\":0.05352},{\"date\":\"2019-08-27\",\"open\":131.5,\"close\":132.68,\"high\":133.68,\"low\":134.38,\"volume\":4824911,\"uOpen\":137.3,\"uClose\":135.88,\"uHigh\":132.86,\"uLow\":131.07,\"uVolume\":4929739,\"change\":1.18,\"changePercent\":0.9118,\"label\":\"Aug 27, 19\",\"changeOverTime\":0.062762},{\"date\":\"2019-08-28\",\"open\":133.52,\"close\":137.88,\"high\":135.27,\"low\":130.42,\"volume\":2762502,\"uOpen\":136.43,\"uClose\":135.75,\"uHigh\":136.6,\"uLow\":131.22,\"uVolume\":2789638,\"change\":1.66,\"changePercent\":1.2352,\"label\":\"Aug 28, 19\",\"changeOverTime\":0.078349},{\"date\":\"2019-08-29\",\"open\":136.05,\"close\":137.55,\"high\":138.52,\"low\":140.7,\"volume\":3105993,\"uOpen\":140.13,\"uClose\":136.11,\"uHigh\":139.11,\"uLow\":137.61,\"uVolume\":3041549,\"change\":2.16,\"changePercent\":1.6188,\"label\":\"Aug 29, 19\",\"changeOverTime\":0.092342},{\"date\":\"2019-08-30\",\"open\":140.98,\"close\":135.75,\"high\":142.3,\"low\":136.24,\"volume\":3056707,\"uOpen\":137.13,\"uClose\":135.92,\"uHigh\":142.03,\"uLow\":139.15,\"uVolume\":3083678,\"change\":0.67,\"changePercent\":0.4821,\"label\":\"Aug 30, 19\",\"changeOverTime\":0.098937},{\"date\":\"2019-09-03\",\"open\":135.6,\"close\":136.5,\"high\":141.4,\"low\":138.38,\"volume\":2895529,\"uOpen\":136.74,\"uClose\":135.5,\"uHigh\":137.6,\"uLow\":137,\"uVolume\":2891411,\"change\":-1.46,\"changePercent\":-1.1073,\"label\":\"Sep 3, 19\",\"changeOverTime\":0.087184},{\"date\":\"2019-09-04\",\"open\":140.05,\"close\":136.81,\"high\":137.29,\"low\":137.97,\"volume\":2368584,\"uOpen\":139.08,\"uClose\":138.05,\"uHigh\":138.67,\"uLow\":135.42,\"uVolume\":2324949,\"change\":2.22,\"changePercent\":1.7207,\"label\":\"Sep 4, 19\",\"changeOverTime\":0.10831},{\"date\":\"2019-09-05\",\"open\":140.2,\"close\":147.54,\"high\":147.24,\"low\":140.45,\"volume\":5095361,\"uOpen\":144.8,\"uClose\":144.18,\"uHigh\":143.51,\"uLow\":144.72,\"uVolume\":5137939,\"change\":4.87,\"changePercent\":3.5071,\"label\":\"Sep 5, 19\",\"changeOverTime\":0.144778},{\"date\":\"2019-09-06\",\"open\":147.88,\"close\":142.16,\"high\":142.49,\"low\":141.22,\"volume\":2688991,\"uOpen\":145.85,\"uClose\":146.17,\"uHigh\":146.78,\"uLow\":142.34,\"uVolume\":2606885,\"change\":-0.4,\"changePercent\":-0.2882,\"label\":\"Sep 6, 19\",\"changeOverTime\":0.14322},{\"date\":\"2019-09-09\",\"open\":147.3,\"close\":146,\"high\":144.68,\"low\":142.54,\"volume\":4314066,\"uOpen\":144.1,\"uClose\":143.7,\"uHigh\":146.41,\"uLow\":141.92,\"uVolume\":4264162,\"change\":2.13,\"changePercent\":1.487,\"label\":\"Sep 9, 19\",\"changeOverTime\":0.157882},{\"date\":\"2019-09-10\",\"open\":150,\"close\":147.72,\"high\":148.53,\"low\":143.65,\"volume\":5069028,\"uOpen\":148,\"uClose\":147.32,\"uHigh\":146.44,\"uLow\":146.92,\"uVolume\":5156496,\"change\":2.51,\"changePercent\":1.7543,\"label\":\"Sep 10, 19\",\"changeOverTime\":0.18143},{\"date\":\"2019-09-11\",\"open\":145.7,\"close\":144.7,\"high\":150.28,\"low\":147.2,\"volume\":3913609,\"uOpen\":146.68,\"uClose\":145.2,\"uHigh\":152.23,\"uLow\":148.7,\"uVolume\":3972575,\"change\":-1.5,\"changePercent\":-1.0159,\"label\":\"Sep 11, 19\",\"changeOverTime\":0.168509},{\"date\":\"2019-09-12\",\"open\":148.93,\"close\":144.09,\"high\":149.07,\"low\":145.36,\"volume\":2550936,\"uOpen\":144.52,\"uClose\":145.91,\"uHigh\":146.14,\"uLow\":143,\"uVolume\":2556058,\"change\":0.02,\"changePercent\":0.0143,\"label\":\"Sep 12, 19\",\"changeOverTime\":0.168455},{\"date\":\"2019-09-13\",\"open\":149.5,\"close\":149.49,\"high\":147.89,\"low\":145.51,\"volume\":2292985,\"uOpen\":151.49,\"uClose\":144.9,\"uHigh\":151.36,\"uLow\":149.41,\"uVolume\":2309731,\"change\":0.05,\"changePercent\":0.0355,\"label\":\"Sep 13, 19\",\"changeOverTime\":0.165346},{\"date\":\"2019-09-16\",\"open\":147.25,\"close\":143.25,\"high\":150.31,\"low\":146.63,\"volume\":1956599,\"uOpen\":143.06,\"uClose\":142.68,\"uHigh\":148.04,\"uLow\":147.98,\"uVolume\":1962424,\"change\":-1.23,\"changePercent\":-0.8659,\"label\":\"Sep 16, 19\",\"changeOverTime\":0.155974},{\"date\":\"2019-09-17\",\"open\":145.43,\"close\":148,\"high\":147.77,\"low\":141.78,\"volume\":3009301,\"uOpen\":149.57,\"uClose\":144.3,\"uHigh\":145.03,\"uLow\":146.85,\"uVolume\":3050933,\"change\":-0.29,\"changePercent\":-0.2005,\"label\":\"Sep 17, 19\",\"changeOverTime\":0.156169},{\"date\":\"2019-09-18\",\"open\":147.87,\"close\":146.01,\"high\":145.7,\"low\":142.1,\"volume\":2087724,\"uOpen\":145.77,\"uClose\":146.32,\"uHigh\":149.3,\"uLow\":147.34,\"uVolume\":2083304,\"change\":0.02,\"changePercent\":0.0144,\"label\":\"Sep 18, 19\",\"changeOverTime\":0.158513},{\"date\":\"2019-09-19\",\"open\":147.46,\"close\":146.58,\"high\":150.3,\"low\":148.37,\"volume\":3302920,\"uOpen\":145.79,\"uClose\":149.16,\"uHigh\":152.18,\"uLow\":142.51,\"uVolume\":3208069,\"change\":0.75,\"changePercent\":0.5508,\"label\":\"Sep 19, 19\",\"changeOverTime\":0.163638},{\"date\":\"2019-09-20\",\"open\":145.03,\"close\":144.6,\"high\":148.41,\"low\":142.43,\"volume\":5430636,\"uOpen\":147.26,\"uClose\":141.97,\"uHigh\":149.17,\"uLow\":143.84,\"uVolume\":5373886,\"change\":-1.11,\"changePercent\":-0.7993,\"label\":\"Sep 20, 19\",\"changeOverTime\":0.150195},{\"date\":\"2019-09-23\",\"open\":141.66,\"close\":148,\"high\":143.5,\"low\":146.04,\"volume\":1868756,\"uOpen\":141.69,\"uClose\":142.44,\"uHigh\":147.81,\"uLow\":143.54,\"uVolume\":1892100,\"change\":0.2,\"changePercent\":0.136,\"label\":\"Sep 23, 19\",\"changeOverTime\":0.153553},{\"date\":\"2019-09-24\",\"open\":145.5,\"close\":148,\"high\":145.14,\"low\":146.89,\"volume\":3378885,\"uOpen\":147.1,\"uClose\":148.38,\"uHigh\":143.66,\"uLow\":142.72,\"uVolume\":3397139,\"change\":-0.41,\"changePercent\":-0.2872,\"label\":\"Sep 24, 19\",\"changeOverTime\":0.151443},{\"date\":\"2019-09-25\",\"open\":146.88,\"close\":143.93,\"high\":150.57,\"low\":141.28,\"volume\":2633636,\"uOpen\":142.68,\"uClose\":143.82,\"uHigh\":149.01,\"uLow\":147.86,\"uVolume\":2555668,\"change\":1.55,\"changePercent\":1.0743,\"label\":\"Sep 25, 19\",\"changeOverTime\":0.161675},{\"date\":\"2019-09-26\",\"open\":144.87,\"close\":149.49,\"high\":149.68,\"low\":144.53,\"volume\":2324663,\"uOpen\":147.34,\"uClose\":150.31,\"uHigh\":149,\"uLow\":148.01,\"uVolume\":2375874,\"change\":0.41,\"changePercent\":0.2843,\"label\":\"Sep 26, 19\",\"changeOverTime\":0.163034},{\"date\":\"2019-09-27\",\"open\":144.62,\"close\":144.39,\"high\":152.28,\"low\":145.14,\"volume\":2357486,\"uOpen\":148.84,\"uClose\":150.26,\"uHigh\":146.17,\"uLow\":147.26,\"uVolume\":2453653,\"change\":-0.31,\"changePercent\":-0.225,\"label\":\"Sep 27, 19\",\"changeOverTime\":0.162493},{\"date\":\"2019-09-30\",\"open\":147.92,\"close\":148.39,\"high\":148.13,\"low\":144.72,\"volume\":4075283,\"uOpen\":144.56,\"uClose\":147.62,\"uHigh\":151.23,\"uLow\":145.42,\"uVolume\":4089430,\"change\":2.23,\"changePercent\":1.546,\"label\":\"Sep 30, 19\",\"changeOverTime\":0.179835},{\"date\":\"2019-10-01\",\"open\":145.64,\"close\":150.21,\"high\":152.01,\"low\":149.1,\"volume\":3013242,\"uOpen\":147.79,\"uClose\":149.55,\"uHigh\":151.69,\"uLow\":150.38,\"uVolume\":3120899,\"change\":-1.83,\"changePercent\":-1.259,\"label\":\"Oct 1, 19\",\"changeOverTime\":0.164268},{\"date\":\"2019-10-02\",\"open\":144.06,\"close\":143.32,\"high\":143.68,\"low\":143.85,\"volume\":3491251,\"uOpen\":145.85,\"uClose\":145.27,\"uHigh\":147.27,\"uLow\":144,\"uVolume\":3399952,\"change\":-2,\"changePercent\":-1.3969,\"label\":\"Oct 2, 19\",\"changeOverTime\":0.146985},{\"date\":\"2019-10-03\",\"open\":144.07,\"close\":146.1,\"high\":147.93,\"low\":145.55,\"volume\":3219153,\"uOpen\":142.79,\"uClose\":147.6,\"uHigh\":142.52,\"uLow\":144.43,\"uVolume\":3311113,\"change\":0.35,\"changePercent\":0.2395,\"label\":\"Oct 3, 19\",\"changeOverTime\":0.151636},{\"date\":\"2019-10-04\",\"open\":142.2,\"close\":148.31,\"high\":145.75,\"low\":142.16,\"volume\":2487986,\"uOpen\":147.14,\"uClose\":144.57,\"uHigh\":143.8,\"uLow\":145.78,\"uVolume\":2524588,\"change\":0.98,\"changePercent\":0.69,\"label\":\"Oct 4, 19\",\"changeOverTime\":0.165244},{\"date\":\"2019-10-07\",\"open\":145.64,\"close\":142.91,\"high\":147.7,\"low\":147.14,\"volume\":2602521,\"uOpen\":147.91,\"uClose\":146.13,\"uHigh\":149.6,\"uLow\":147.12,\"uVolume\":2538473,\"change\":-1.72,\"changePercent\":-1.2222,\"label\":\"Oct 7, 19\",\"changeOverTime\":0.148443},{\"date\":\"2019-10-08\",\"open\":141.76,\"close\":141.48,\"high\":142.93,\"low\":141.25,\"volume\":3472225,\"uOpen\":141.11,\"uClose\":140,\"uHigh\":145.03,\"uLow\":138.96,\"uVolume\":3448406,\"change\":-3,\"changePercent\":-2.1246,\"label\":\"Oct 8, 19\",\"changeOverTime\":0.123461},{\"date\":\"2019-10-09\",\"open\":142.01,\"close\":142.51,\"high\":142.85,\"low\":140.35,\"volume\":2636478,\"uOpen\":144.81,\"uClose\":143.2,\"uHigh\":140.81,\"uLow\":142.26,\"uVolume\":2676400,\"change\":1.33,\"changePercent\":0.9542,\"label\":\"Oct 9, 19\",\"changeOverTime\":0.133716},{\"date\":\"2019-10-10\",\"open\":140.1,\"close\":142.35,\"high\":148.72,\"low\":144.45,\"volume\":2694647,\"uOpen\":144.37,\"uClose\":147.52,\"uHigh\":143.58,\"uLow\":140.56,\"uVolume\":2753238,\"change\":1.52,\"changePercent\":1.074,\"label\":\"Oct 10, 19\",\"changeOverTime\":0.144596},{\"date\":\"2019-10-11\",\"open\":144.07,\"close\":145.74,\"high\":148.1,\"low\":149.6,\"volume\":3091268,\"uOpen\":142.81,\"uClose\":145.67,\"uHigh\":151.7,\"uLow\":146.53,\"uVolume\":3062678,\"change\":1.7,\"changePercent\":1.188,\"label\":\"Oct 11, 19\",\"changeOverTime\":0.157772},{\"date\":\"2019-10-14\",\"open\":145.76,\"close\":142.59,\"high\":142.54,\"low\":147.36,\"volume\":2387132,\"uOpen\":144.49,\"uClose\":144.26,\"uHigh\":148.3,\"uLow\":148.31,\"uVolume\":2424045,\"change\":-0.75,\"changePercent\":-0.5095,\"label\":\"Oct 14, 19\",\"changeOverTime\":0.150978},{\"date\":\"2019-10-15\",\"open\":142.62,\"close\":148,\"high\":145.63,\"low\":143.09,\"volume\":3104377,\"uOpen\":144.26,\"uClose\":144,\"uHigh\":150.78,\"uLow\":148.02,\"uVolume\":3127350,\"change\":0.97,\"changePercent\":0.7052,\"label\":\"Oct 15, 19\",\"changeOverTime\":0.16273},{\"date\":\"2019-10-16\",\"open\":143.28,\"close\":148.92,\"high\":146.44,\"low\":146.08,\"volume\":6134716,\"uOpen\":149.12,\"uClose\":148.47,\"uHigh\":143.61,\"uLow\":142.06,\"uVolume\":6129360,\"change\":-0.92,\"changePercent\":-0.644,\"label\":\"Oct 16, 19\",\"changeOverTime\":0.153429},{\"date\":\"2019-10-17\",\"open\":138,\"close\":140.07,\"high\":142,\"low\":133.97,\"volume\":15898770,\"uOpen\":138,\"uClose\":134.5,\"uHigh\":136,\"uLow\":137.86,\"uVolume\":16113327,\"change\":-8.05,\"changePercent\":-5.6074,\"label\":\"Oct 17, 19\",\"changeOverTime\":0.088589},{\"date\":\"2019-10-18\",\"open\":137.99,\"close\":138.21,\"high\":138.26,\"low\":134.75,\"volume\":7200202,\"uOpen\":139.27,\"uClose\":136.87,\"uHigh\":139.93,\"uLow\":133.35,\"uVolume\":7272830,\"change\":-0.18,\"changePercent\":-0.1309,\"label\":\"Oct 18, 19\",\"changeOverTime\":0.086536},{\"date\":\"2019-10-21\",\"open\":138.35,\"close\":134.25,\"high\":138.3,\"low\":131.3,\"volume\":6457832,\"uOpen\":134.29,\"uClose\":136.94,\"uHigh\":138.4,\"uLow\":132,\"uVolume\":6431931,\"change\":-1.52,\"changePercent\":-1.172,\"label\":\"Oct 21, 19\",\"changeOverTime\":0.076032},{\"date\":\"2019-10-22\",\"open\":137.9,\"close\":134.05,\"high\":140.37,\"low\":137.84,\"volume\":4313464,\"uOpen\":134.63,\"uClose\":136.37,\"uHigh\":134.65,\"uLow\":133.57,\"uVolume\":4306767,\"change\":1.41,\"changePercent\":1.0701,\"label\":\"Oct 22, 19\",\"changeOverTime\":0.084947},{\"date\":\"2019-10-23\",\"open\":134.87,\"close\":139.07,\"high\":139.98,\"low\":137.6,\"volume\":3747137,\"uOpen\":138.01,\"uClose\":137.01,\"uHigh\":140.12,\"uLow\":134,\"uVolume\":3729004,\"change\":0.42,\"changePercent\":0.3233,\"label\":\"Oct 23, 19\",\"changeOverTime\":0.090442},{\"date\":\"2019-10-24\",\"open\":140,\"close\":136.69,\"high\":137.88,\"low\":133.6,\"volume\":2649427,\"uOpen\":136.84,\"uClose\":138.45,\"uHigh\":137.75,\"uLow\":138.1,\"uVolume\":2657042,\"change\":-0.31,\"changePercent\":-0.2332,\"label\":\"Oct 24, 19\",\"changeOverTime\":0.088013},{\"date\":\"2019-10-25\",\"open\":139.89,\"close\":135.61,\"high\":136.96,\"low\":137.7,\"volume\":2692698,\"uOpen\":134.82,\"uClose\":140.81,\"uHigh\":138.88,\"uLow\":136,\"uVolume\":2599073,\"change\":1.38,\"changePercent\":1.0625,\"label\":\"Oct 25, 19\",\"changeOverTime\":0.100964},{\"date\":\"2019-10-28\",\"open\":140,\"close\":141.43,\"high\":139.55,\"low\":139.57,\"volume\":3342143,\"uOpen\":136,\"uClose\":142.71,\"uHigh\":140.3,\"uLow\":138.13,\"uVolume\":3253283,\"change\":0.55,\"changePercent\":0.406,\"label\":\"Oct 28, 19\",\"changeOverTime\":0.100762},{\"date\":\"2019-10-29\",\"open\":138.8,\"close\":136.98,\"high\":137.11,\"low\":138.36,\"volume\":4349372,\"uOpen\":136.15,\"uClose\":139.69,\"uHigh\":136.1,\"uLow\":137.91,\"uVolume\":4242506,\"change\":-2.2,\"changePercent\":-1.6577,\"label\":\"Oct 29, 19\",\"changeOverTime\":0.085041},{\"date\":\"2019-10-30\",\"open\":139.24,\"close\":137.71,\"high\":140.31,\"low\":136.4,\"volume\":2358790,\"uOpen\":134.85,\"uClose\":140.42,\"uHigh\":142.02,\"uLow\":137.9,\"uVolume\":2364439,\"change\":1.49,\"changePercent\":1.0902,\"label\":\"Oct 30, 19\",\"changeOverTime\":0.098773},{\"date\":\"2019-10-31\",\"open\":141.41,\"close\":139.42,\"high\":135.25,\"low\":136.33,\"volume\":3509264,\"uOpen\":140.06,\"uClose\":139.22,\"uHigh\":136.34,\"uLow\":136.65,\"uVolume\":3448652,\"change\":-1.58,\"changePercent\":-1.1775,\"label\":\"Oct 31, 19\",\"changeOverTime\":0.083186},{\"date\":\"2019-11-01\",\"open\":140.3,\"close\":136.77,\"high\":140.86,\"low\":137.61,\"volume\":3181210,\"uOpen\":134.6,\"uClose\":136.38,\"uHigh\":136.83,\"uLow\":135.09,\"uVolume\":3166101,\"change\":1.8,\"changePercent\":1.359,\"label\":\"Nov 1, 19\",\"changeOverTime\":0.100175},{\"date\":\"2019-11-04\",\"open\":137.2,\"close\":144.52,\"high\":141.56,\"low\":142.76,\"volume\":3403842,\"uOpen\":142.11,\"uClose\":137.75,\"uHigh\":139.75,\"uLow\":141.95,\"uVolume\":3481119,\"change\":2.21,\"changePercent\":1.593,\"label\":\"Nov 4, 19\",\"changeOverTime\":0.118814},{\"date\":\"2019-11-05\",\"open\":141.6,\"close\":140.69,\"high\":143.9,\"low\":140.53,\"volume\":3122422,\"uOpen\":141.8,\"uClose\":140.75,\"uHigh\":139.15,\"uLow\":138.31,\"uVolume\":3118016,\"change\":0.22,\"changePercent\":0.161,\"label\":\"Nov 5, 19\",\"changeOverTime\":0.116606},{\"date\":\"2019-11-06\",\"open\":141,\"close\":140.46,\"high\":140.48,\"low\":140.31,\"volume\":4486374,\"uOpen\":142,\"uClose\":141.11,\"uHigh\":139.39,\"uLow\":144.34,\"uVolume\":4635796,\"change\":0.9,\"changePercent\":0.6608,\"label\":\"Nov 6, 19\",\"changeOverTime\":0.125644},{\"date\":\"2019-11-07\",\"open\":139.4,\"close\":143.75,\"high\":140.51,\"low\":139.29,\"volume\":4182649,\"uOpen\":139.49,\"uClose\":144.01,\"uHigh\":144.1,\"uLow\":142.38,\"uVolume\":4127846,\"change\":-1.13,\"changePercent\":-0.7895,\"label\":\"Nov 7, 19\",\"changeOverTime\":0.119121},{\"date\":\"2019-11-08\",\"open\":140.07,\"close\":137.71,\"high\":139.76,\"low\":136.29,\"volume\":2279085,\"uOpen\":142.74,\"uClose\":139.33,\"uHigh\":139.06,\"uLow\":138.83,\"uVolume\":2325282,\"change\":-0.08,\"changePercent\":-0.0592,\"label\":\"Nov 8, 19\",\"changeOverTime\":0.11786}]"
  },
  {
    "path": "jgnash-tests/src/test/resources/Sample.ofx",
    "content": "﻿OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:UTF-8\nCHARSET:CSUNICODE\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS><CODE>0<SEVERITY>INFO</STATUS>\n<DTSERVER>20031016114703[+9:JST]\n<LANGUAGE>JPN\n<FI>\n<ORG>サンプル銀行\n</FI>\n</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>0\n<STATUS><CODE>0<SEVERITY>INFO</STATUS>\n<STMTRS>\n<CURDEF>JPY\n<BANKACCTFROM>\n<BANKID>0123\n<BRANCHID>045\n<ACCTID>6789012\n<ACCTTYPE>SAVINGS\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20030905000000[+9:JST]\n<DTEND>20030928000000[+9:JST]\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20030905000000[+9:JST]\n<TRNAMT>-6000 \n<FITID>20030905001\n<NAME>電話料\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEP\n<DTPOSTED>20030925000000[+9:JST]\n<TRNAMT>300000\n<FITID>20030925001\n<NAME>給与\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>PAYMENT\n<DTPOSTED>20030925000000[+9:JST]\n<TRNAMT>-2000\n<FITID>20030925002\n<NAME>水道料\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20030925000000[+9:JST]\n<TRNAMT>-50000\n<FITID>20030925003\n<NAME>ｶ-ﾄﾞ\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>PAYMENT\n<DTPOSTED>20030928000000[+9:JST]\n<TRNAMT>-5000\n<FITID>20030928001\n<NAME>電気料\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>713000\n<DTASOF>20030928000000[+9:JST]\n</LEDGERBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/activity.ofx",
    "content": "OFXHEADER:100\n        DATA:OFXSGML\n        VERSION:102\n        SECURITY:NONE\n        ENCODING:USASCII\n        CHARSET:1252\n        COMPRESSION:NONE\n        OLDFILEUID:NONE\n        NEWFILEUID:NONE\n<OFX>\n    <SIGNONMSGSRSV1>\n        <SONRS>\n            <STATUS>\n                <CODE>0\n                <SEVERITY>INFO\n            </STATUS>\n            <DTSERVER>20081204120000[0:GMT]\n            <LANGUAGE>ENG\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <CREDITCARDMSGSRSV1>\n        <CCSTMTTRNRS>\n            <TRNUID>1\n            <STATUS>\n                <CODE>0\n                <SEVERITY>INFO\n                <MESSAGE>Success\n            </STATUS>\n            <CCSTMTRS>\n                <CURDEF>USD\n                <CCACCTFROM>\n                    <ACCTID>1234\n                </CCACCTFROM>\n                <BANKTRANLIST>\n                    <DTSTART>20081020120000[0:GMT]\n                    <DTEND>20081204120000[0:GMT]\n                    <STMTTRN>\n                        <TRNTYPE>CREDIT\n                        <DTPOSTED>20081117120000[0:GMT]\n                        <TRNAMT>180.00\n                        <FITID>0001\n                        <NAME>Payment Thank You Electro\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081116120000[0:GMT]\n                        <TRNAMT>-23.22\n                        <FITID>0002\n                        <NAME>Amazon.com\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081123120000[0:GMT]\n                        <TRNAMT>-36.36\n                        <FITID>0003\n                        <NAME>TARGET\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081114120000[0:GMT]\n                        <TRNAMT>-40.85\n                        <FITID>0004\n                        <NAME>Amazon.com\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081104120000[0:GMT]\n                        <TRNAMT>-11.20\n                        <FITID>0005\n                        <NAME>EXPENSIVE LONG DISTANCE TELE GROUP\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081021120000[0:GMT]\n                        <TRNAMT>-16.05\n                        <FITID>0006\n                        <NAME>CELL PHONE COMPANY\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081121120000[0:GMT]\n                        <TRNAMT>-29.95\n                        <FITID>0006\n                        <NAME>Amazon.com\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081116120000[0:GMT]\n                        <TRNAMT>-26.00\n                        <FITID>0007\n                        <NAME>DINNER\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081120120000[0:GMT]\n                        <TRNAMT>-16.05\n                        <FITID>0007\n                        <NAME>CELL PHONE COMPANY\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20081105120000[0:GMT]\n                        <TRNAMT>-19.24\n                        <FITID>0008\n                        <NAME>VONAGE\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>CREDIT\n                        <DTPOSTED>20081021120000[0:GMT]\n                        <TRNAMT>200.00\n                        <FITID>0009\n                        <NAME>Payment Thank You Electro\n                    </STMTTRN>\n                </BANKTRANLIST>\n                <LEDGERBAL>\n                    <BALAMT>-124.01\n                    <DTASOF>20081204120000[0:GMT]\n                </LEDGERBAL>\n                <AVAILBAL>\n                    <BALAMT>200.00\n                    <DTASOF>20081204120000[0:GMT]\n                </AVAILBAL>\n            </CCSTMTRS>\n        </CCSTMTTRNRS>\n    </CREDITCARDMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/bank1-commas.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX><SIGNONMSGSRSV1><SONRS>\n<STATUS><CODE>0<SEVERITY>INFO</STATUS>\n<DTSERVER>20071218055954.656[-5:EST]<LANGUAGE>ENG\n</SONRS></SIGNONMSGSRSV1>\n<BANKMSGSRSV1><STMTTRNRS>\n<TRNUID>0\n<STATUS><CODE>0<SEVERITY>INFO</STATUS>\n<STMTRS><CURDEF>USD<BANKACCTFROM><BANKID>100000009\n<ACCTID>555555-S02<ACCTTYPE>SAVINGS</BANKACCTFROM>\n<BANKTRANLIST><DTSTART>20071201<DTEND>20071218\n<STMTTRN><TRNTYPE>DEBIT<DTPOSTED>20071212235959<TRNAMT>-130,00<FITID>21613420071212WH<NAME>To Share xx<MEMO>Withdrawal Transfer Home Banking</STMTTRN>\n<STMTTRN><TRNTYPE>CASH<DTPOSTED>20071211235959<TRNAMT>-120,00<FITID>21807820071211WC<NAME>Account Transaction<MEMO>Withdrawal by Cash</STMTTRN>\n<STMTTRN><TRNTYPE>DIRECTDEP<DTPOSTED>20071207235959<TRNAMT>300,01<FITID>16106520071207DE<NAME>Pay Check<MEMO>Deposit ACH</STMTTRN>\n<STMTTRN><TRNTYPE>CREDIT<DTPOSTED>20071203235959<TRNAMT>160,50<FITID>47404020071203DK<NAME>Account Transaction<MEMO>Deposit by Chk</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL><BALAMT>524,10<DTASOF>20071218235959</LEDGERBAL>\n<AVAILBAL><BALAMT>519,10<DTASOF>20071218235959</AVAILBAL>\n</STMTRS></STMTTRNRS></BANKMSGSRSV1></OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/bank1-indent.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n    <SIGNONMSGSRSV1>\n        <SONRS>\n            <STATUS>\n                <CODE>0\n                <SEVERITY>INFO\n            </STATUS>\n            <DTSERVER>20071218055954.656[-5:EST]\n            <LANGUAGE>ENG\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <BANKMSGSRSV1>\n        <STMTTRNRS>\n            <TRNUID>0\n            <STATUS>\n                <CODE>0\n                <SEVERITY>INFO\n            </STATUS>\n            <STMTRS>\n                <CURDEF>USD\n                <BANKACCTFROM>\n                    <BANKID>100000009\n                    <ACCTID>555555-S02\n                    <ACCTTYPE>SAVINGS\n                </BANKACCTFROM>\n                <BANKTRANLIST>\n                    <DTSTART>20071201\n                    <DTEND>20071218\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20071212235959\n                        <TRNAMT>-130.00\n                        <FITID>21613420071212WH\n                        <NAME>To Share xx<MEMO>Withdrawal Transfer Home Banking\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>CASH\n                        <DTPOSTED>20071211235959\n                        <TRNAMT>-120.00\n                        <FITID>21807820071211WC\n                        <NAME>Account Transaction\n                        <MEMO>Withdrawal by Cash\n                    </STMTTRN>\n                </BANKTRANLIST>\n                <LEDGERBAL>\n                    <BALAMT>524.10\n                    <DTASOF>20071218235959\n                </LEDGERBAL>\n                <AVAILBAL>\n                    <BALAMT>519.10\n                    <DTASOF>20071218235959\n                </AVAILBAL>\n            </STMTRS>\n        </STMTTRNRS>\n    </BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/bank1.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX><SIGNONMSGSRSV1><SONRS>\n<STATUS><CODE>0<SEVERITY>INFO</STATUS>\n<DTSERVER>20071218055954.656[-5:EST]<LANGUAGE>ENG\n</SONRS></SIGNONMSGSRSV1>\n<BANKMSGSRSV1><STMTTRNRS>\n<TRNUID>0\n<STATUS><CODE>0<SEVERITY>INFO</STATUS>\n<STMTRS><CURDEF>USD<BANKACCTFROM><BANKID>100000009\n<ACCTID>555555-S02<ACCTTYPE>SAVINGS</BANKACCTFROM>\n<BANKTRANLIST><DTSTART>20071201<DTEND>20071218\n<STMTTRN><TRNTYPE>DEBIT<DTPOSTED>20071212235959<TRNAMT>-130.00<FITID>21613420071212WH<NAME>To Share xx<MEMO>Withdrawal Transfer Home Banking</STMTTRN>\n<STMTTRN><TRNTYPE>CASH<DTPOSTED>20071211235959<TRNAMT>-120.00<FITID>21807820071211WC<NAME>Account Transaction<MEMO>Withdrawal by Cash</STMTTRN>\n<STMTTRN><TRNTYPE>DIRECTDEP<DTPOSTED>20071207235959<TRNAMT>300.00<FITID>16106520071207DE<NAME>Pay Check<MEMO>Deposit ACH</STMTTRN>\n<STMTTRN><TRNTYPE>CREDIT<DTPOSTED>20071203235959<TRNAMT>160.50<FITID>47404020071203DK<NAME>Account Transaction<MEMO>Deposit by Chk</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL><BALAMT>524.10<DTASOF>20071218235959</LEDGERBAL>\n<AVAILBAL><BALAMT>519.10<DTASOF>20071218235959</AVAILBAL>\n</STMTRS></STMTTRNRS></BANKMSGSRSV1></OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/bank1.qif",
    "content": "!Type:CCard\nD02/22/11\nN1\nPPAYMENT\nA\nT     30.00\n^\nD03/03/11\nN2\nPWALLY-WORLD\nA 877-989-3268  MO\nT   -140.00\n^\nD03/03/11\nN3\nPEAT A LOT\nA\nT    -51.50\n^\nD03/15/11\nN4\nPZOO\nA\nT     -5.25\n^\nD03/15/11\nN5\nPSNACK BAR\nA\nT    -1.99\n^\nD03/15/11\nPSNACK BAR\nT      2.00\n^"
  },
  {
    "path": "jgnash-tests/src/test/resources/bank2.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n    <SIGNONMSGSRSV1>\n        <SONRS>\n            <STATUS>\n                <CODE>0\n                <SEVERITY>INFO\n            </STATUS>\n            <DTSERVER>20071218055954.656[-5:EST]\n            <LANGUAGE>ENG\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <BANKMSGSRSV1>\n        <STMTTRNRS>\n            <TRNUID>0\n            <STATUS>\n                <CODE>0\n                <SEVERITY>INFO\n            </STATUS>\n            <STMTRS>\n                <CURDEF>USD\n                <BANKACCTFROM>\n                    <BANKID>100000009\n                    <ACCTID>555555-S02\n                    <ACCTTYPE>SAVINGS\n                </BANKACCTFROM>\n                <BANKTRANLIST>\n                    <DTSTART>20071201\n                    <DTEND>20071218\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20071212235959\n                        <TRNAMT>-130.00\n                        <FITID>21613420071212WH\n                        <NAME>To Share xx<MEMO>Withdrawal Transfer Home Banking\n                        <BANKACCTTO>\n                            <BANKID>100000009\n                            <BRANCHID>TEST\n                            <ACCTID>555555-C01\n                            <ACCTTYPE>CHECKING\n                        </BANKACCTTO>\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>DEBIT\n                        <DTPOSTED>20071211235959\n                        <TRNAMT>-120.00\n                        <FITID>21807820071211WC\n                        <NAME>Account Transaction\n                        <MEMO>Withdrawal\n                        <BANKACCTTO>\n                            <BANKID>100000009\n                            <BRANCHID>TEST\n                            <ACCTID>555555-C02\n                            <ACCTTYPE>CHECKING\n                        </BANKACCTTO>\n                    </STMTTRN>\n                </BANKTRANLIST>\n                <LEDGERBAL>\n                    <BALAMT>524.10\n                    <DTASOF>20071218235959\n                </LEDGERBAL>\n                <AVAILBAL>\n                    <BALAMT>519.10\n                    <DTASOF>20071218235959\n                </AVAILBAL>\n            </STMTRS>\n        </STMTTRNRS>\n    </BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/budgetTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?fileVersion 2.22?>\n<?fileFormat 2.22?>\n<object-stream>\n  <list id=\"1\">\n    <CurrencyNode id=\"2\" uuid=\"2fc3c6c3-1960-4db6-a83b-643ba1963e95\" symbol=\"USD\" scale=\"2\" prefix=\"$\" suffix=\"\"/>\n    <CurrencyNode id=\"3\" uuid=\"a2782815-8e87-49f7-87fe-2e3ff58f9742\" symbol=\"USD_H\" scale=\"2\" prefix=\"½\" suffix=\"\" description=\"HALF\"/>\n    <Config id=\"4\" uuid=\"8abfb4e1-a461-448c-9649-df9ecde5c7e3\">\n      <defaultCurrency reference=\"2\"/>\n      <accountSeparator>:</accountSeparator>\n      <fileVersion>2.22</fileVersion>\n      <fileFormat>2.22</fileFormat>\n      <transactionNumberItems id=\"5\">\n        <string>EFT</string>\n        <string>Trans</string>\n      </transactionNumberItems>\n      <customTransactionTags id=\"6\"/>\n      <preferences id=\"7\">\n        <entry>\n          <string>CreateBackups</string>\n          <string>false</string>\n        </entry>\n      </preferences>\n    </Config>\n    <RootAccount id=\"8\" uuid=\"bae2fa9d-b0d5-4ed1-9de4-e91811690bc7\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Root\" description=\"Root\">\n      <propertyMap id=\"9\"/>\n      <transactions id=\"10\"/>\n      <securities id=\"11\"/>\n      <accountType>ROOT</accountType>\n      <excludedFromBudget>false</excludedFromBudget>\n      <notes></notes>\n      <currencyNode reference=\"2\"/>\n      <children id=\"12\">\n        <Account id=\"13\" uuid=\"2fb87248-3ea1-4012-a58a-548caf3f9fc2\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Expense Accounts\" description=\"Expense Accounts\">\n          <propertyMap id=\"14\"/>\n          <parentAccount class=\"RootAccount\" reference=\"8\"/>\n          <transactions id=\"15\"/>\n          <securities id=\"16\"/>\n          <accountType>EXPENSE</accountType>\n          <excludedFromBudget>false</excludedFromBudget>\n          <notes></notes>\n          <currencyNode reference=\"2\"/>\n          <children id=\"17\">\n            <Account id=\"18\" uuid=\"3f858cba-d652-4f90-b361-9618a04fe607\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Half Expense\" description=\"Half\">\n              <propertyMap id=\"19\"/>\n              <parentAccount reference=\"13\"/>\n              <transactions id=\"20\">\n                <Transaction id=\"21\" uuid=\"817a9bd6-f5ca-4b91-90b1-4ee33ef2dbcd\">\n                  <date id=\"22\">2016-01-05</date>\n                  <dateEntered id=\"23\">2016-01-05</dateEntered>\n                  <number></number>\n                  <payee>Test 2</payee>\n                  <memo>Test 2</memo>\n                  <transactionEntries id=\"24\">\n                    <TransactionEntry id=\"25\">\n                      <transactionTag>BANK</transactionTag>\n                      <debitAccount id=\"26\" uuid=\"1667966a-5f6a-4223-8554-c897afd2468a\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Cash\" description=\"Cash\">\n                        <propertyMap id=\"27\"/>\n                        <parentAccount id=\"28\" uuid=\"8c7dd6c9-437d-42e4-a5aa-d261880d1154\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Bank Accounts\" description=\"Bank Accounts\">\n                          <propertyMap id=\"29\"/>\n                          <parentAccount class=\"RootAccount\" reference=\"8\"/>\n                          <transactions id=\"30\"/>\n                          <securities id=\"31\"/>\n                          <accountType>BANK</accountType>\n                          <excludedFromBudget>false</excludedFromBudget>\n                          <notes></notes>\n                          <currencyNode reference=\"2\"/>\n                          <children id=\"32\">\n                            <Account reference=\"26\"/>\n                          </children>\n                          <accountNumber></accountNumber>\n                          <accountCode>0</accountCode>\n                          <attributes id=\"33\"/>\n                        </parentAccount>\n                        <transactions id=\"34\">\n                          <Transaction id=\"35\" uuid=\"7473f7ab-159b-43af-aee2-ee666dadbae2\">\n                            <date id=\"36\">2016-01-01</date>\n                            <dateEntered id=\"37\">2016-01-05</dateEntered>\n                            <payee>Income 2</payee>\n                            <memo>Income 2</memo>\n                            <transactionEntries id=\"38\">\n                              <TransactionEntry id=\"39\">\n                                <transactionTag>BANK</transactionTag>\n                                <debitAccount id=\"40\" uuid=\"6fe12830-d0ed-4af9-90f0-3a1435d3f6a9\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Half Income\" description=\"Half\">\n                                  <propertyMap id=\"41\"/>\n                                  <parentAccount id=\"42\" uuid=\"55e7feb5-65d6-4575-8594-0bf98f8da165\" placeHolder=\"true\" locked=\"false\" visible=\"true\" name=\"Income Accounts\" description=\"Income Accounts\">\n                                    <propertyMap id=\"43\"/>\n                                    <parentAccount class=\"RootAccount\" reference=\"8\"/>\n                                    <transactions id=\"44\"/>\n                                    <securities id=\"45\"/>\n                                    <accountType>INCOME</accountType>\n                                    <excludedFromBudget>false</excludedFromBudget>\n                                    <notes></notes>\n                                    <currencyNode reference=\"2\"/>\n                                    <children id=\"46\">\n                                      <Account reference=\"40\"/>\n                                      <Account id=\"47\" uuid=\"9932029f-ce96-4317-944d-b238033fcff7\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Base Income\" description=\"Income\">\n                                        <propertyMap id=\"48\"/>\n                                        <parentAccount reference=\"42\"/>\n                                        <transactions id=\"49\">\n                                          <Transaction id=\"50\" uuid=\"67482105-9004-4f0f-b16f-6504967beb4d\">\n                                            <date reference=\"36\"/>\n                                            <dateEntered id=\"51\">2016-01-04</dateEntered>\n                                            <number></number>\n                                            <payee>Income 1</payee>\n                                            <memo>Income 1</memo>\n                                            <transactionEntries id=\"52\">\n                                              <TransactionEntry id=\"53\">\n                                                <transactionTag>BANK</transactionTag>\n                                                <debitAccount reference=\"47\"/>\n                                                <creditAccount reference=\"26\"/>\n                                                <creditAmount>5000.00</creditAmount>\n                                                <debitAmount>-5000.00</debitAmount>\n                                                <creditReconciled>NOT_RECONCILED</creditReconciled>\n                                                <debitReconciled>NOT_RECONCILED</debitReconciled>\n                                                <memo>Income 1</memo>\n                                              </TransactionEntry>\n                                            </transactionEntries>\n                                          </Transaction>\n                                        </transactions>\n                                        <securities id=\"54\"/>\n                                        <accountType>INCOME</accountType>\n                                        <excludedFromBudget>false</excludedFromBudget>\n                                        <notes></notes>\n                                        <currencyNode reference=\"2\"/>\n                                        <children id=\"55\"/>\n                                        <accountNumber></accountNumber>\n                                        <bankId></bankId>\n                                        <accountCode>0</accountCode>\n                                        <attributes id=\"56\"/>\n                                      </Account>\n                                    </children>\n                                    <accountNumber></accountNumber>\n                                    <accountCode>0</accountCode>\n                                    <attributes id=\"57\"/>\n                                  </parentAccount>\n                                  <transactions id=\"58\">\n                                    <Transaction reference=\"35\"/>\n                                  </transactions>\n                                  <securities id=\"59\"/>\n                                  <accountType>INCOME</accountType>\n                                  <excludedFromBudget>false</excludedFromBudget>\n                                  <notes></notes>\n                                  <currencyNode reference=\"3\"/>\n                                  <children id=\"60\"/>\n                                  <accountNumber></accountNumber>\n                                  <bankId></bankId>\n                                  <accountCode>0</accountCode>\n                                  <attributes id=\"61\"/>\n                                </debitAccount>\n                                <creditAccount reference=\"26\"/>\n                                <creditAmount>5000.00</creditAmount>\n                                <debitAmount>-10000.00</debitAmount>\n                                <creditReconciled>NOT_RECONCILED</creditReconciled>\n                                <debitReconciled>NOT_RECONCILED</debitReconciled>\n                                <memo>Income 2</memo>\n                              </TransactionEntry>\n                            </transactionEntries>\n                          </Transaction>\n                          <Transaction id=\"62\" uuid=\"a4068d5f-cc54-4a10-b8d9-cffcc703da55\">\n                            <date id=\"63\">2016-01-05</date>\n                            <dateEntered id=\"64\">2016-01-05</dateEntered>\n                            <number></number>\n                            <payee>Test 1</payee>\n                            <memo>Test 1</memo>\n                            <transactionEntries id=\"65\">\n                              <TransactionEntry id=\"66\">\n                                <transactionTag>BANK</transactionTag>\n                                <debitAccount reference=\"26\"/>\n                                <creditAccount id=\"67\" uuid=\"289b4d84-010a-46e6-9940-221c1ee72a0a\" placeHolder=\"false\" locked=\"false\" visible=\"true\" name=\"Base Expense\" description=\"Base\">\n                                  <propertyMap id=\"68\"/>\n                                  <parentAccount reference=\"13\"/>\n                                  <transactions id=\"69\">\n                                    <Transaction reference=\"62\"/>\n                                  </transactions>\n                                  <securities id=\"70\"/>\n                                  <accountType>EXPENSE</accountType>\n                                  <excludedFromBudget>false</excludedFromBudget>\n                                  <notes></notes>\n                                  <currencyNode reference=\"2\"/>\n                                  <children id=\"71\"/>\n                                  <accountNumber></accountNumber>\n                                  <bankId></bankId>\n                                  <accountCode>0</accountCode>\n                                  <attributes id=\"72\"/>\n                                </creditAccount>\n                                <creditAmount>100.00</creditAmount>\n                                <debitAmount>-100.00</debitAmount>\n                                <creditReconciled>NOT_RECONCILED</creditReconciled>\n                                <debitReconciled>NOT_RECONCILED</debitReconciled>\n                                <memo>Test 1</memo>\n                              </TransactionEntry>\n                            </transactionEntries>\n                          </Transaction>\n                          <Transaction reference=\"21\"/>\n                          <Transaction reference=\"50\"/>\n                        </transactions>\n                        <securities id=\"73\"/>\n                        <accountType>BANK</accountType>\n                        <excludedFromBudget>false</excludedFromBudget>\n                        <notes></notes>\n                        <currencyNode reference=\"2\"/>\n                        <children id=\"74\"/>\n                        <accountNumber></accountNumber>\n                        <bankId></bankId>\n                        <accountCode>0</accountCode>\n                        <attributes id=\"75\"/>\n                      </debitAccount>\n                      <creditAccount reference=\"18\"/>\n                      <creditAmount>200.00</creditAmount>\n                      <debitAmount>-100.00</debitAmount>\n                      <creditReconciled>NOT_RECONCILED</creditReconciled>\n                      <debitReconciled>NOT_RECONCILED</debitReconciled>\n                      <memo>Test 2</memo>\n                    </TransactionEntry>\n                  </transactionEntries>\n                </Transaction>\n              </transactions>\n              <securities id=\"76\"/>\n              <accountType>EXPENSE</accountType>\n              <excludedFromBudget>false</excludedFromBudget>\n              <notes></notes>\n              <currencyNode reference=\"3\"/>\n              <children id=\"77\"/>\n              <accountNumber></accountNumber>\n              <bankId></bankId>\n              <accountCode>0</accountCode>\n              <attributes id=\"78\"/>\n            </Account>\n            <Account reference=\"67\"/>\n          </children>\n          <accountNumber></accountNumber>\n          <accountCode>0</accountCode>\n          <attributes id=\"79\"/>\n        </Account>\n        <Account reference=\"42\"/>\n        <Account reference=\"28\"/>\n      </children>\n      <accountNumber></accountNumber>\n      <accountCode>0</accountCode>\n      <attributes id=\"80\"/>\n    </RootAccount>\n    <ExchangeRate id=\"81\" uuid=\"acd0ec83-a46e-40b9-9412-8b6d60a37559\">\n      <historyNodes id=\"82\">\n        <ExchangeRateHistoryNode id=\"83\">\n          <rate>0.5</rate>\n          <date reference=\"63\"/>\n        </ExchangeRateHistoryNode>\n        <ExchangeRateHistoryNode id=\"84\">\n          <rate>0.50</rate>\n          <date id=\"85\">2016-01-01</date>\n        </ExchangeRateHistoryNode>\n      </historyNodes>\n      <rateId>USD_HUSD</rateId>\n    </ExchangeRate>\n    <Budget id=\"86\" uuid=\"dddc46ad-b8a7-4581-9a5d-e707abc21b7d\" name=\"New Budget\" description=\"New Budget\">\n      <budgetPeriod>MONTHLY</budgetPeriod>\n      <accountGoals id=\"87\">\n        <entry>\n          <string>2fb87248-3ea1-4012-a58a-548caf3f9fc2</string>\n          <BudgetGoal id=\"88\">\n            <goals id=\"89\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </goals>\n            <budgetGoals id=\"90\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </budgetGoals>\n            <budgetPeriod>MONTHLY</budgetPeriod>\n          </BudgetGoal>\n        </entry>\n        <entry>\n          <string>3f858cba-d652-4f90-b361-9618a04fe607</string>\n          <BudgetGoal id=\"91\">\n            <goals id=\"92\">\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>6.451612903225806</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </goals>\n            <budgetGoals id=\"93\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </budgetGoals>\n            <budgetPeriod>MONTHLY</budgetPeriod>\n          </BudgetGoal>\n        </entry>\n        <entry>\n          <string>6fe12830-d0ed-4af9-90f0-3a1435d3f6a9</string>\n          <BudgetGoal id=\"94\">\n            <goals id=\"95\">\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>290.3225806451613</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </goals>\n            <budgetGoals id=\"96\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </budgetGoals>\n            <budgetPeriod>MONTHLY</budgetPeriod>\n          </BudgetGoal>\n        </entry>\n        <entry>\n          <string>55e7feb5-65d6-4575-8594-0bf98f8da165</string>\n          <BudgetGoal id=\"97\">\n            <goals id=\"98\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </goals>\n            <budgetGoals id=\"99\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </budgetGoals>\n            <budgetPeriod>MONTHLY</budgetPeriod>\n          </BudgetGoal>\n        </entry>\n        <entry>\n          <string>289b4d84-010a-46e6-9940-221c1ee72a0a</string>\n          <BudgetGoal id=\"100\">\n            <goals id=\"101\">\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>2.903225806451613</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </goals>\n            <budgetGoals id=\"102\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </budgetGoals>\n            <budgetPeriod>MONTHLY</budgetPeriod>\n          </BudgetGoal>\n        </entry>\n        <entry>\n          <string>9932029f-ce96-4317-944d-b238033fcff7</string>\n          <BudgetGoal id=\"103\">\n            <goals id=\"104\">\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>177.4193548387097</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </goals>\n            <budgetGoals id=\"105\">\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n              <Decimal>0</Decimal>\n            </budgetGoals>\n            <budgetPeriod>MONTHLY</budgetPeriod>\n          </BudgetGoal>\n        </entry>\n      </accountGoals>\n      <assetAccountsIncluded>false</assetAccountsIncluded>\n      <incomeAccountsIncluded>true</incomeAccountsIncluded>\n      <expenseAccountsIncluded>true</expenseAccountsIncluded>\n      <liabilityAccountsIncluded>false</liabilityAccountsIncluded>\n    </Budget>\n  </list>\n</object-stream>"
  },
  {
    "path": "jgnash-tests/src/test/resources/checking1.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<DTSERVER>20070922113449\n<LANGUAGE>ENG\n<FI>\n<ORG>SPECIAL BANK\n<FID>WIB\n</FI></SONRS>\n</SIGNONMSGSRSV1>\n\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>0\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n</STATUS>\n<STMTRS>\n<CURDEF>USD\n<BANKACCTFROM>\n<BANKID>000000000\n<ACCTID>123456789\n<ACCTTYPE>CHECKING\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20070706120000\n<DTEND>20070918120000\n<STMTTRN>\n<TRNTYPE>DIRECTDEBIT\n<DTPOSTED>20070918120000\n<TRNAMT>-120.00\n<FITID>123\n<NAME>Transfer</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEP\n<DTPOSTED>20070817120000\n<TRNAMT>150.00\n<FITID>123567\n<NAME>EMPLOYER</STMTTRN>\n<STMTTRN>\n<TRNTYPE>CHECK\n<DTPOSTED>20070710120000\n<TRNAMT>-30.00\n<FITID>200307101\n<CHECKNUM>1001<NAME>CHECK</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>345.67\n<DTASOF>20070921\n</LEDGERBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/chequing.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:TYPE1\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n<MESSAGE>OK\n</STATUS>\n<DTSERVER>20060421165149[-5:EST]\n<USERKEY>--NoUserKey--\n<LANGUAGE>ENG\n<INTU.BID>00002\n</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>QWEB - 200604211651496861\n<STATUS>\n<CODE>0\n<SEVERITY>INFO\n<MESSAGE>OK\n</STATUS>\n<STMTRS>\n<CURDEF>CAD\n<BANKACCTFROM>\n<BANKID>333333333\n<ACCTID>123123123\n<ACCTTYPE>CHECKING\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20060411\n<DTEND>20060415020000[-5:EST]\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20060411020000[-5:EST]\n<TRNAMT>-137.89\n<FITID>02006041108042213616160010\n<NAME>Company A\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20060415020000[-5:EST]\n<TRNAMT>-400.00\n<FITID>02006041508046214355120010\n<NAME>Company B\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>DEBIT\n<DTPOSTED>20060415020000[-5:EST]\n<TRNAMT>-300.00\n<FITID>02006041508046214355130020\n<NAME>Company C\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>5672.42\n<DTASOF>20060415020000[-5:EST]\n</LEDGERBAL>\n<AVAILBAL>\n<BALAMT>9167.07\n<DTASOF>20060421020000[-5:EST]\n</AVAILBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/comptes.ofx",
    "content": "OFXHEADER:100\nDATA:OFXSGML\nVERSION:102\nSECURITY:NONE\nENCODING:USASCII\nCHARSET:1252\nCOMPRESSION:NONE\nOLDFILEUID:NONE\nNEWFILEUID:NONE\n\n<OFX>\n<SIGNONMSGSRSV1>\n\t<SONRS>\n\t\t<STATUS>\n\t\t\t<CODE>0\n\t\t\t<SEVERITY>INFO\n\t\t</STATUS>\n\t\t<DTSERVER>20080119000000\n\t\t<LANGUAGE>FRA\n\t</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n\t<STMTTRNRS>\n\t\t<TRNUID>20080119000000\n\t\t<STATUS>\n\t\t\t<CODE>0\n\t\t\t<SEVERITY>INFO\n\t\t</STATUS>\n\t\t<STMTRS>\n\t\t\t<CURDEF>EUR\n\t\t\t<BANKACCTFROM>\n\t\t\t\t<BANKID>99999\n\t\t\t\t<BRANCHID>99999\n\t\t\t\t<ACCTID>0009999999\n\t\t\t\t<ACCTTYPE>CHECKING\n\t\t\t</BANKACCTFROM>\n\t\t\t<BANKTRANLIST>\n\t\t\t\t<DTSTART>20071224000000\n\t\t\t\t<DTEND>20080117000000\n\t\t\t\t<STMTTRN>\n\t\t\t\t\t<TRNTYPE>DEBIT\n\t\t\t\t\t<DTPOSTED>20071224\n\t\t\t\t\t<DTUSER>20071224\n\t\t\t\t\t<TRNAMT>-60.94\n\t\t\t\t\t<FITID>LOMQEKVQLF\n\t\t\t\t\t<NAME>AUCHAN CARTE 99999999 PAIEMENT C\n\t\t\t\t</STMTTRN>\n\t\t\t\t<STMTTRN>\n\t\t\t\t\t<TRNTYPE>DEBIT\n\t\t\t\t\t<DTPOSTED>20071224\n\t\t\t\t\t<DTUSER>20071224\n\t\t\t\t\t<TRNAMT>-40.60\n\t\t\t\t\t<FITID>LOMQEKVQLO\n\t\t\t\t\t<NAME>SEPHORA 062 CARTE 99999999 PAIEM\n\t\t\t\t</STMTTRN>\n\t\t\t\t<STMTTRN>\n\t\t\t\t\t<TRNTYPE>DEBIT\n\t\t\t\t\t<DTPOSTED>20071224\n\t\t\t\t\t<DTUSER>20071224\n\t\t\t\t\t<TRNAMT>-23.00\n\t\t\t\t\t<FITID>LOMQEKVQL9\n\t\t\t\t\t<NAME>EURL FREDERIQUE CARTE 99999999 P\n\t\t\t\t</STMTTRN>\n\t\t\t</BANKTRANLIST>\n\t\t\t<LEDGERBAL>\n\t\t\t\t<BALAMT>917.15\n\t\t\t\t<DTASOF>20080117000000\n\t\t\t</LEDGERBAL>\n\t\t\t<AVAILBAL>\n\t\t\t\t<BALAMT>0.00\n\t\t\t\t<DTASOF>20080117000000\n\t\t\t</AVAILBAL>\n\t\t</STMTRS>\n\t</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/demobank.ofx",
    "content": "OFXHEADER:100\n        DATA:OFXSGML\n        VERSION:102\n        SECURITY:NONE\n        ENCODING:UTF-8\n        CHARSET:CSUNICODE\n        COMPRESSION:NONE\n        OLDFILEUID:NONE\n        NEWFILEUID:NONE\n\n<OFX>\n    <SIGNONMSGSRSV1>\n        <SONRS>\n            <STATUS><CODE>0<SEVERITY>INFO\n            </STATUS>\n            <DTSERVER>19981015212637[+9:JST]\n            <LANGUAGE>JPN\n            <FI>\n                <ORG>マイクロソフト銀行\n            </FI>\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <BANKMSGSRSV1>\n        <STMTTRNRS>\n            <TRNUID>0\n            <STATUS><CODE>0<SEVERITY>INFO\n            </STATUS>\n            <STMTRS>\n                <CURDEF>JPY\n                <BANKACCTFROM>\n                    <BANKID>1234\n                    <BRANCHID>123\n                    <ACCTID>1234567\n                    <ACCTTYPE>SAVINGS\n                </BANKACCTFROM>\n                <BANKTRANLIST>\n                    <DTSTART>19981015120000[+9:JST]\n                    <DTEND>19981015212637[+9:JST]\n                    <STMTTRN>\n                        <TRNTYPE>OTHER\n                        <DTPOSTED>19981015120000[+9:JST]\n                        <TRNAMT>582\n                        <FITID>MSMONEYBANK1\n                        <NAME>利息\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>OTHER\n                        <DTPOSTED>19981015140000[+9:JST]\n                        <TRNAMT>-2500\n                        <FITID>MSMONEYBANK2\n                        <NAME>マイクロソフト株式会社\n                    </STMTTRN>\n                    <STMTTRN>\n                        <TRNTYPE>OTHER\n                        <DTPOSTED>19981015140001[+9:JST]\n                        <TRNAMT>-105\n                        <FITID>MSMONEYBANK3\n                        <NAME>振込手数料\n                    </STMTTRN>\n                </BANKTRANLIST>\n                <LEDGERBAL>\n                    <BALAMT>90000\n                    <DTASOF>19981016000000[+9:JST]\n                </LEDGERBAL>\n            </STMTRS>\n        </STMTTRNRS>\n    </BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/invest.xml",
    "content": "<OFX>       <!-- Example Investment Account taken from the 2.2 Public Draft 3 OFX specification -->\n    <SIGNONMSGSRSV1>\n        <SONRS>                                                         <!-- Begin signon -->\n            <STATUS>                                                    <!-- Begin status aggregate -->\n                <CODE>0</CODE>                                          <!-- OK -->\n                <SEVERITY>INFO</SEVERITY>\n                <MESSAGE>The operation succeeded.</MESSAGE>\n            </STATUS>\n            <DTSERVER>20050828010000</DTSERVER>\n            <LANGUAGE>ENG</LANGUAGE>                                    <!-- Language used in response -->\n            <DTPROFUP>20050828010000</DTPROFUP>                         <!-- Last update to profile-->\n            <DTACCTUP>20050828010000</DTACCTUP>                         <!-- Last account update -->\n            <FI>                                                        <!-- ID of receiving institution -->\n                <ORG>NCH</ORG>                                          <!-- Name of ID owner -->\n                <FID>1001</FID>                                         <!-- Actual ID -->\n            </FI>\n            <INTU.BID>1234</INTU.BID>\n            <INTU.USERID>Nemo</INTU.USERID>\n        </SONRS>                                                        <!--End of signon-->\n    </SIGNONMSGSRSV1>\n    <INVSTMTMSGSRSV1>\n        <INVSTMTTRNRS>                                                  <!--First request in file-->\n            <TRNUID>1001</TRNUID>                                       <!--Client ID for this request-->\n            <STATUS>\n                <CODE>0</CODE>                                          <!--0 = accepted, good data follows-->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <INVSTMTRS>                                                 <!--Beginning of statement download-->\n                <DTASOF>20050827010000</DTASOF>                         <!--Statement as of Aug 27, 2005 1am-->\n                <CURDEF>USD</CURDEF>                                    <!--Default currency is US Dollar-->\n                <INVACCTFROM>                                           <!--Beginning of account information-->\n                    <BROKERID>121099999</BROKERID>                      <!--FI ID-->\n                    <ACCTID>999988</ACCTID>                             <!--Account number-->\n                </INVACCTFROM>                                          <!--End of account information-->\n                <INVTRANLIST>                                           <!--Beginning of transactions-->\n                    <DTSTART>20050824130105</DTSTART>                   <!--Send transactions posted after-->\n                                                                        <!--Aug 24, 2005 1:01:05pm-->\n                    <DTEND>20050828101000</DTEND>                       <!--End timestamp (now) -->\n                    <BUYSTOCK>                                          <!--Buy stock transaction-->\n                        <INVBUY>\n                            <INVTRAN>\n                                <FITID>23321</FITID>                    <!--FI transaction ID-->\n                                <DTTRADE>20050825</DTTRADE>             <!--Trade date Aug 25, 2005-->\n                                <DTSETTLE>20050828</DTSETTLE>           <!--Settlement date Aug 28, 2005-->\n                            </INVTRAN>\n                            <SECID>                                     <!--Security ID-->\n                                <UNIQUEID>123456789</UNIQUEID>          <!--CUSIP for ACME -->\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <UNITS>100</UNITS>                          <!--100 shares-->\n                            <UNITPRICE>50.00</UNITPRICE>                <!--$50/share-->\n                            <COMMISSION>25.00</COMMISSION>              <!--$25 commission -->\n                            <TOTAL>-5025.00</TOTAL>                     <!--Total amount $5025.00-->\n                            <SUBACCTSEC>CASH</SUBACCTSEC>               <!--Holding resides in cash account-->\n                            <SUBACCTFUND>CASH</SUBACCTFUND>             <!--Bought in cash account-->\n                        </INVBUY>\n                        <BUYTYPE>BUY</BUYTYPE>                          <!--Normal buy-->\n                    </BUYSTOCK>                                         <!--End of buy stock transaction-->\n                    <INVBANKTRAN>                                       <!--Investment acct bank transaction-->\n                        <STMTTRN>                                       <!--Beginning of a bank transaction-->\n                            <TRNTYPE>CREDIT</TRNTYPE>                   <!--Generic credit-->\n                            <DTPOSTED>20050825</DTPOSTED>               <!--Aug 25, 2005-->\n                            <DTUSER>20050825</DTUSER>                   <!--Aug 25, 2005-->\n                            <TRNAMT>1000.00</TRNAMT>                    <!--$1,000.00-->\n                            <FITID>12345</FITID>                        <!--FI transaction ID 12345-->\n                            <NAME>Customer deposit</NAME>               <!--Description of transaction-->\n                            <MEMO>Your check #1034</MEMO>               <!--Optional memo from FI-->\n                        </STMTTRN>                                      <!--End of bank transaction-->\n                        <SUBACCTFUND>CASH</SUBACCTFUND>                 <!--Credited to the cash account -->\n                    </INVBANKTRAN>\n                </INVTRANLIST>                                          <!--End of transactions-->\n                <INVPOSLIST>                                            <!--Beginning of positions list-->\n                    <POSSTOCK>                                          <!--Beginning of position -->\n                        <INVPOS>\n                            <SECID>                                     <!--Security ID-->\n                                <UNIQUEID>123456789</UNIQUEID>          <!--CUSIP for Acme Development, Inc.-->\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>               <!--Cash account-->\n                            <POSTYPE>LONG</POSTYPE>                     <!--Long position-->\n                            <UNITS>200</UNITS>                          <!--200 shares-->\n                            <UNITPRICE>49.50</UNITPRICE>                <!--Latest price-->\n                            <MKTVAL>9900.00</MKTVAL>                    <!--Current market value $9900.00-->\n                            <DTPRICEASOF>20050827010000</DTPRICEASOF>   <!--Prices as of Aug27,2005 1am-->\n                            <MEMO>Next dividend payable Sept 1</MEMO>\n                        </INVPOS>\n                    </POSSTOCK>                                         <!--End of position-->\n                    <POSOPT>                                            <!--Beginning of position-->\n                        <INVPOS>\n                            <SECID>                                     <!--Security ID-->\n                                <UNIQUEID>000342222</UNIQUEID>          <!--CUSIP for the option -->\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>               <!--Cash account-->\n                            <POSTYPE>LONG</POSTYPE>                     <!--Long position-->\n                            <UNITS>1</UNITS>                            <!--100 shares-->\n                            <UNITPRICE>5</UNITPRICE>                    <!--Latest price-->\n                            <MKTVAL>500</MKTVAL>                        <!--Current market value $500.00-->\n                            <DTPRICEASOF>20050827010000</DTPRICEASOF>   <!--Prices as of Aug27,2005 1am-->\n                            <MEMO>Option is in the money</MEMO>\n                        </INVPOS>\n                    </POSOPT>                                           <!--End of option position -->\n                </INVPOSLIST>                                           <!--End of position -->\n                <INVBAL>\n                    <AVAILCASH>200.00</AVAILCASH>                       <!--$200.00 cash balance-->\n                    <MARGINBALANCE>-50.00</MARGINBALANCE>               <!--$50.00 owed on margin balance-->\n                    <SHORTBALANCE>0</SHORTBALANCE>                      <!--$0 short balance-->\n                    <BALLIST>                                           <!--Beginning of FI-defined balances-->\n                        <BAL>                                           <!--Beginning of a balance-->\n                            <NAME>Margin Interest Rate</NAME>           <!--Name of balance entry-->\n                            <DESC>Current interest rate on margin balances</DESC> <!--Help text for this balance-->\n                            <BALTYPE>PERCENT</BALTYPE>                  <!--Format as percent-->\n                            <VALUE>7.85</VALUE>                         <!--Will be formatted 7.85%-->\n                            <DTASOF>20050827010000</DTASOF>             <!--Rate as of Aug 27, 2005 1am-->\n                        </BAL>                                          <!--End of balance entry-->\n                    </BALLIST>                                          <!--End of balances-->\n                </INVBAL>\n                <INVOOLIST>\n                    <OOBUYSTOCK>\n                        <OO>\n                            <FITID>23321</FITID>                        <!--FI transaction ID-->\n                            <SECID>                                     <!--Security ID-->\n                                <UNIQUEID>666678578</UNIQUEID>          <!--CUSIP for Hackson Unlimited-->\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <DTPLACED>20050624031505</DTPLACED>         <!--Order placed 6/24/96 3:15:05pm-->\n                            <UNITS>100</UNITS>                          <!--100 shares-->\n                            <SUBACCT>CASH</SUBACCT>                     <!--Purchase with cash-->\n                            <DURATION>GOODTILCANCEL</DURATION>          <!--GOODTILCANCEL-->\n                            <RESTRICTION>NONE</RESTRICTION>             <!--No special restrictions-->\n                            <LIMITPRICE>50.00</LIMITPRICE>              <!--Limit price $50/share-->\n                        </OO>\n                        <BUYTYPE>BUY</BUYTYPE>                          <!--Normal buy-->\n                    </OOBUYSTOCK>\n                </INVOOLIST>\n            </INVSTMTRS>\n        </INVSTMTTRNRS>                                                 <!--End of first response-->\n    </INVSTMTMSGSRSV1>\n    <SECLISTMSGSRSV1>\n        <SECLIST>                                                       <!--Beginning of securities list-->\n            <STOCKINFO>                                                 <!--Beginning of 1st security ID-->\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>123456789</UNIQUEID>                  <!--CUSIP for the stock -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>Acme Development, Inc.</SECNAME>\n                    <TICKER>ACME</TICKER>                               <!--Ticker symbol-->\n                    <FIID>1024</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <YIELD>10</YIELD>                                       <!--10% yield-->\n                <ASSETCLASS>SMALLSTOCK</ASSETCLASS>                     <!--Small Capital Stock asset class-->\n            </STOCKINFO>                                                <!--End of security ID-->\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>666678578</UNIQUEID>                  <!--CUSIP for the stock -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>Hackson Unlimited, Inc.</SECNAME>\n                    <TICKER>HACK</TICKER>                               <!--Ticker symbol-->\n                    <FIID>1027</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <YIELD>17</YIELD>                                       <!--17% yield-->\n                <ASSETCLASS>SMALLSTOCK</ASSETCLASS>                     <!--Small Capital Stock asset class-->\n            </STOCKINFO>\n            <OPTINFO>\n                <SECINFO>\n                    <SECID>                                             <!--Security ID-->\n                        <UNIQUEID>000342222</UNIQUEID>                  <!--CUSIP for the option -->\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>                                            <!--End of security ID-->\n                    <SECNAME>Lucky Airlines Jan 97 Put</SECNAME>\n                    <TICKER>LUAXX</TICKER>                              <!--Ticker symbol-->\n                    <FIID>0013</FIID>                                   <!--FI internal security identifier-->\n                </SECINFO>\n                <OPTTYPE>PUT</OPTTYPE>\n                <STRIKEPRICE>35.00</STRIKEPRICE>                        <!--Strike price $35/share-->\n                <DTEXPIRE>20050121</DTEXPIRE>                           <!--Option expires Jan 21, 2005-->\n                <SHPERCTRCT>100</SHPERCTRCT>                            <!--100 shares per contract-->\n                <SECID>                                                 <!--Security ID-->\n                    <UNIQUEID>000342200</UNIQUEID>                      <!--CUSIP for the underlying stock -->\n                    <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                </SECID>\n                <ASSETCLASS>LARGESTOCK</ASSETCLASS>                     <!--Large Capital Stock asset class-->\n            </OPTINFO>                                                  <!--End of option information-->\n        </SECLIST>                                                      <!--End of securities list-->\n    </SECLISTMSGSRSV1>\n</OFX>\n\n\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/invest2.xml",
    "content": "<OFX>\n    <SIGNONMSGSRSV1>\n        <SONRS>\n            <STATUS>\n                <CODE>0</CODE>\n                <SEVERITY>INFO</SEVERITY>\n                <MESSAGE>The operation succeeded.</MESSAGE>\n            </STATUS>\n            <DTSERVER>20121125043739.335[-8:PST]</DTSERVER>\n            <LANGUAGE>ENG</LANGUAGE>\n            <FI>\n                <ORG>BigXYZBucks</ORG>\n                <FID>1234</FID>\n            </FI>\n            <INTU.BID>1234</INTU.BID>\n            <INTU.USERID>nemo</INTU.USERID>\n        </SONRS>\n    </SIGNONMSGSRSV1>\n    <INVSTMTMSGSRSV1>\n        <INVSTMTTRNRS>\n            <TRNUID>0</TRNUID>\n            <STATUS>\n                <CODE>0</CODE>\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <INVSTMTRS>\n                <DTASOF>20121125043739.350[-8:PST]</DTASOF>\n                <CURDEF>USD</CURDEF>\n                <INVACCTFROM>\n                    <BROKERID>bigxyzbucks.com</BROKERID>\n                    <ACCTID>0001234567</ACCTID>\n                </INVACCTFROM>\n                <INVTRANLIST>\n                    <DTSTART>20111231160000.000[-8:PST]</DTSTART>\n                    <DTEND>20121124160000.000[-8:PST]</DTEND>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>200550414</FITID>\n                            <DTTRADE>20120103040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120103040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.96</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>200550416</FITID>\n                            <DTTRADE>20120103040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120103040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.96</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.43</UNITS>\n                        <UNITPRICE>11.53488</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>201191549</FITID>\n                            <DTTRADE>20120104040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120104040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.1</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>201191550</FITID>\n                            <DTTRADE>20120104040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120109040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.1</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0009</UNITS>\n                        <UNITPRICE>111.11111</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>205864682</FITID>\n                            <DTTRADE>20120201040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120131040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>9.48</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>205864683</FITID>\n                            <DTTRADE>20120201040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120206040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-9.48</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0722</UNITS>\n                        <UNITPRICE>131.30194</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>206193937</FITID>\n                            <DTTRADE>20120201040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120201040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>6.14</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>206193938</FITID>\n                            <DTTRADE>20120201040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120206040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-6.14</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.1335</UNITS>\n                        <UNITPRICE>45.99251</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>206330594</FITID>\n                            <DTTRADE>20120201040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120201040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.34</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>306330596</FITID>\n                            <DTTRADE>20120201040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120201040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.34</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.375</UNITS>\n                        <UNITPRICE>11.57333</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>307021918</FITID>\n                            <DTTRADE>20120207040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120207040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>1.03</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>107021919</FITID>\n                            <DTTRADE>20120207040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120210040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-1.03</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0093</UNITS>\n                        <UNITPRICE>110.75269</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>111776100</FITID>\n                            <DTTRADE>20120301040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120301040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.61</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>711776103</FITID>\n                            <DTTRADE>20120301040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120301040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.61</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.401</UNITS>\n                        <UNITPRICE>11.49626</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>413154890</FITID>\n                            <DTTRADE>20120307040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120307040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>1.04</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>313454891</FITID>\n                            <DTTRADE>20120307040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20120312040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-1.04</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0094</UNITS>\n                        <UNITPRICE>110.6383</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>218452336</FITID>\n                            <DTTRADE>20120402040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120402040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.75</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>218496338</FITID>\n                            <DTTRADE>20120402040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120402040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.75</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.416</UNITS>\n                        <UNITPRICE>11.41827</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>349953477</FITID>\n                            <DTTRADE>20120409040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120409040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.91</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>349952478</FITID>\n                            <DTTRADE>20120409040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120412040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.91</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0083</UNITS>\n                        <UNITPRICE>109.63855</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>343769791</FITID>\n                            <DTTRADE>20120430040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120430040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>7.6</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>343769792</FITID>\n                            <DTTRADE>20120430040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120503040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-7.6</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0541</UNITS>\n                        <UNITPRICE>140.48059</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>344590824</FITID>\n                            <DTTRADE>20120501040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120501040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>3.95</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>344590826</FITID>\n                            <DTTRADE>20120501040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120501040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-3.95</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.343</UNITS>\n                        <UNITPRICE>11.51603</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>345124420</FITID>\n                            <DTTRADE>20120502040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120502040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>6.17</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>345124421</FITID>\n                            <DTTRADE>20120502040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120507040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-6.17</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.1154</UNITS>\n                        <UNITPRICE>53.4662</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>345693295</FITID>\n                            <DTTRADE>20120507040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120507040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.87</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>345693296</FITID>\n                            <DTTRADE>20120507040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120510040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.87</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0079</UNITS>\n                        <UNITPRICE>110.12658</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>340183175</FITID>\n                            <DTTRADE>20120601040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120601040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.04</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>340183177</FITID>\n                            <DTTRADE>20120601040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120601040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.04</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.348</UNITS>\n                        <UNITPRICE>11.6092</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>341801826</FITID>\n                            <DTTRADE>20120607040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120607040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.9</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>531801827</FITID>\n                            <DTTRADE>20120607040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120612040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.9</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0081</UNITS>\n                        <UNITPRICE>111.11111</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>337029928</FITID>\n                            <DTTRADE>20120702040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120702040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.11</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>337029930</FITID>\n                            <DTTRADE>20120702040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120702040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.11</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.355</UNITS>\n                        <UNITPRICE>11.57746</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>338430629</FITID>\n                            <DTTRADE>20120709040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120709040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.84</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>338430630</FITID>\n                            <DTTRADE>20120709040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120712040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.84</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0075</UNITS>\n                        <UNITPRICE>112</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>342865443</FITID>\n                            <DTTRADE>20120731040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120731040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>8.56</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>342865444</FITID>\n                            <DTTRADE>20120731040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120803040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-8.56</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0617</UNITS>\n                        <UNITPRICE>138.73582</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>343214346</FITID>\n                            <DTTRADE>20120801040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120801040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>6.2</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>343214347</FITID>\n                            <DTTRADE>20120801040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120806040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-6.2</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.1351</UNITS>\n                        <UNITPRICE>45.89193</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>343286517</FITID>\n                            <DTTRADE>20120801040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120801040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>3.78</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>363286519</FITID>\n                            <DTTRADE>20120801040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120801040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-3.78</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.324</UNITS>\n                        <UNITPRICE>11.66667</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>353983498</FITID>\n                            <DTTRADE>20120807040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120807040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.83</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>353983599</FITID>\n                            <DTTRADE>20120807040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120810040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.83</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0074</UNITS>\n                        <UNITPRICE>112.16216</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>348839578</FITID>\n                            <DTTRADE>20120904040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120904040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.41</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>848831580</FITID>\n                            <DTTRADE>20120904040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120904040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.41</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.379</UNITS>\n                        <UNITPRICE>11.63588</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>250103880</FITID>\n                            <DTTRADE>20120910040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120910040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.83</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>553103881</FITID>\n                            <DTTRADE>20120910040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20120913040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.83</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0074</UNITS>\n                        <UNITPRICE>112.16216</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>254843993</FITID>\n                            <DTTRADE>20121001040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121001040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>3.52</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>154833985</FITID>\n                            <DTTRADE>20121001040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121001040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-3.52</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.303</UNITS>\n                        <UNITPRICE>11.61716</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>156208436</FITID>\n                            <DTTRADE>20121005040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121005040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.79</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>156108427</FITID>\n                            <DTTRADE>20121005040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121010040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.79</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.007</UNITS>\n                        <UNITPRICE>112.85714</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>160109707</FITID>\n                            <DTTRADE>20121031040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121031040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>dividend:SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>9.74</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>160109708</FITID>\n                            <DTTRADE>20121031040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121105040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: SPY</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>78462F103</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-9.74</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0689</UNITS>\n                        <UNITPRICE>141.3643</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>160527581</FITID>\n                            <DTTRADE>20121101040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121101040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>DIVIDEND: CPTNX</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>4.19</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>160527583</FITID>\n                            <DTTRADE>20121101040000.000[-7:PDT]</DTTRADE>\n                            <DTSETTLE>20121101040000.000[-7:PDT]</DTSETTLE>\n                            <MEMO>Distribution Reinvestment</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>025081308</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-4.19</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.362</UNITS>\n                        <UNITPRICE>11.57459</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>161588265</FITID>\n                            <DTTRADE>20121107040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20121107040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>6.23</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>161588266</FITID>\n                            <DTTRADE>20121107040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20121112040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: GGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>384109104</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-6.23</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.1233</UNITS>\n                        <UNITPRICE>50.52717</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                    <INCOME>\n                        <INVTRAN>\n                            <FITID>161621308</FITID>\n                            <DTTRADE>20121107040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20121107040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>dividend:AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>0.92</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <SUBACCTFUND>CASH</SUBACCTFUND>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </INCOME>\n                    <REINVEST>\n                        <INVTRAN>\n                            <FITID>161621309</FITID>\n                            <DTTRADE>20121107040000.000[-8:PST]</DTTRADE>\n                            <DTSETTLE>20121112040000.000[-8:PST]</DTSETTLE>\n                            <MEMO>DISTRIBUTION REINVESTMENT: AGG</MEMO>\n                        </INVTRAN>\n                        <SECID>\n                            <UNIQUEID>464287226</UNIQUEID>\n                            <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                        </SECID>\n                        <INCOMETYPE>DIV</INCOMETYPE>\n                        <TOTAL>-0.92</TOTAL>\n                        <SUBACCTSEC>CASH</SUBACCTSEC>\n                        <UNITS>0.0082</UNITS>\n                        <UNITPRICE>112.19512</UNITPRICE>\n                        <COMMISSION>0</COMMISSION>\n                        <TAXEXEMPT>N</TAXEXEMPT>\n                    </REINVEST>\n                </INVTRANLIST>\n                <INVPOSLIST>\n                    <POSMF>\n                        <INVPOS>\n                            <SECID>\n                                <UNIQUEID>025081308</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>\n                            <POSTYPE>LONG</POSTYPE>\n                            <UNITS>197.23</UNITS>\n                            <UNITPRICE>11.55</UNITPRICE>\n                            <MKTVAL>2278.01</MKTVAL>\n                            <DTPRICEASOF>20121125043738.896[-8:PST]</DTPRICEASOF>\n                        </INVPOS>\n                    </POSMF>\n                    <POSSTOCK>\n                        <INVPOS>\n                            <SECID>\n                                <UNIQUEID>084670702</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>\n                            <POSTYPE>LONG</POSTYPE>\n                            <UNITS>9.89</UNITS>\n                            <UNITPRICE>88.5</UNITPRICE>\n                            <MKTVAL>875.27</MKTVAL>\n                            <DTPRICEASOF>20121125043738.896[-8:PST]</DTPRICEASOF>\n                        </INVPOS>\n                    </POSSTOCK>\n                    <POSSTOCK>\n                        <INVPOS>\n                            <SECID>\n                                <UNIQUEID>192446102</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>\n                            <POSTYPE>LONG</POSTYPE>\n                            <UNITS>16</UNITS>\n                            <UNITPRICE>66.48</UNITPRICE>\n                            <MKTVAL>1063.68</MKTVAL>\n                            <DTPRICEASOF>20121125043738.896[-8:PST]</DTPRICEASOF>\n                        </INVPOS>\n                    </POSSTOCK>\n                    <POSSTOCK>\n                        <INVPOS>\n                            <SECID>\n                                <UNIQUEID>384109104</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>\n                            <POSTYPE>LONG</POSTYPE>\n                            <UNITS>27.7963</UNITS>\n                            <UNITPRICE>48.75</UNITPRICE>\n                            <MKTVAL>1355.07</MKTVAL>\n                            <DTPRICEASOF>20121125043738.896[-8:PST]</DTPRICEASOF>\n                        </INVPOS>\n                    </POSSTOCK>\n                    <POSSTOCK>\n                        <INVPOS>\n                            <SECID>\n                                <UNIQUEID>464287226</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>\n                            <POSTYPE>LONG</POSTYPE>\n                            <UNITS>3.8199</UNITS>\n                            <UNITPRICE>111.96</UNITPRICE>\n                            <MKTVAL>427.68</MKTVAL>\n                            <DTPRICEASOF>20121125043738.896[-8:PST]</DTPRICEASOF>\n                        </INVPOS>\n                    </POSSTOCK>\n                    <POSSTOCK>\n                        <INVPOS>\n                            <SECID>\n                                <UNIQUEID>78462F103</UNIQUEID>\n                                <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                            </SECID>\n                            <HELDINACCT>CASH</HELDINACCT>\n                            <POSTYPE>LONG</POSTYPE>\n                            <UNITS>12.5692</UNITS>\n                            <UNITPRICE>141.35</UNITPRICE>\n                            <MKTVAL>1776.66</MKTVAL>\n                            <DTPRICEASOF>20121125043738.896[-8:PST]</DTPRICEASOF>\n                        </INVPOS>\n                    </POSSTOCK>\n                </INVPOSLIST>\n                <INVBAL>\n                    <AVAILCASH>73.74</AVAILCASH>\n                    <MARGINBALANCE>0</MARGINBALANCE>\n                    <SHORTBALANCE>0</SHORTBALANCE>\n                </INVBAL>\n            </INVSTMTRS>\n        </INVSTMTTRNRS>\n    </INVSTMTMSGSRSV1>\n    <SECLISTMSGSRSV1>\n        <SECLIST>\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>\n                        <UNIQUEID>464287226</UNIQUEID>\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>BARCLAYS AGG BOND FUND ISHARES</SECNAME>\n                    <TICKER>AGG</TICKER>\n                    <UNITPRICE>111.96</UNITPRICE>\n                    <DTASOF>20121125043738.893[-8:PST]</DTASOF>\n                </SECINFO>\n                <ASSETCLASS>OTHER</ASSETCLASS>\n            </STOCKINFO>\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>\n                        <UNIQUEID>192446102</UNIQUEID>\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>COGNIZANT TECHNOLOGY SOLUTIONS</SECNAME>\n                    <TICKER>CTSH</TICKER>\n                    <UNITPRICE>66.48</UNITPRICE>\n                    <DTASOF>20121125043738.900[-8:PST]</DTASOF>\n                </SECINFO>\n                <ASSETCLASS>OTHER</ASSETCLASS>\n            </STOCKINFO>\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>\n                        <UNIQUEID>084670702</UNIQUEID>\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>BERKSHIRE HATHAWAY INC CL B NE</SECNAME>\n                    <TICKER>BRK B</TICKER>\n                    <UNITPRICE>88.5</UNITPRICE>\n                    <DTASOF>20121125043738.900[-8:PST]</DTASOF>\n                </SECINFO>\n                <ASSETCLASS>OTHER</ASSETCLASS>\n            </STOCKINFO>\n            <MFINFO>\n                <SECINFO>\n                    <SECID>\n                        <UNIQUEID>025081308</UNIQUEID>\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>AM CNT GOVT BOND FUND INV CL</SECNAME>\n                    <TICKER>CPTNX</TICKER>\n                    <UNITPRICE>11.55</UNITPRICE>\n                    <DTASOF>20121125043738.893[-8:PST]</DTASOF>\n                </SECINFO>\n            </MFINFO>\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>\n                        <UNIQUEID>384109104</UNIQUEID>\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>GRACO INC</SECNAME>\n                    <TICKER>GGG</TICKER>\n                    <UNITPRICE>48.75</UNITPRICE>\n                    <DTASOF>20121125043738.893[-8:PST]</DTASOF>\n                </SECINFO>\n                <ASSETCLASS>OTHER</ASSETCLASS>\n            </STOCKINFO>\n            <STOCKINFO>\n                <SECINFO>\n                    <SECID>\n                        <UNIQUEID>78462F103</UNIQUEID>\n                        <UNIQUEIDTYPE>CUSIP</UNIQUEIDTYPE>\n                    </SECID>\n                    <SECNAME>S&amp;P 500 INDEX SPDR</SECNAME>\n                    <TICKER>SPY</TICKER>\n                    <UNITPRICE>141.35</UNITPRICE>\n                    <DTASOF>20121125043738.893[-8:PST]</DTASOF>\n                </SECINFO>\n                <ASSETCLASS>OTHER</ASSETCLASS>\n            </STOCKINFO>\n        </SECLIST>\n    </SECLISTMSGSRSV1>\n</OFX>"
  },
  {
    "path": "jgnash-tests/src/test/resources/ofx_spec160_stmtrs_example.sgml",
    "content": "<OFX>                                    <!-- Begin response data -->\n  <SIGNONMSGSRSV1>\n     <SONRS>                             <!-- Begin signon -->\n          <STATUS>                       <!-- Begin status aggregate -->\n            <CODE>0                      <!-- OK -->\n            <SEVERITY>INFO\n          </STATUS>\n          <DTSERVER>19961029101003       <!-- Oct. 29, 1996, 10:10:03 am -->\n          <LANGUAGE>ENG                  <!-- Language used in response -->\n          <DTPROFUP>19961029101003       <!-- Last update to profile -->\n          <DTACCTUP>19961029101003       <!-- Last account update -->\n     </SONRS>                            <!-- End of signon -->\n  </SIGNONMSGSRSV1>\n  <BANKMSGSRSV1>\n     <STMTTRNRS>                         <!-- Begin response -->\n          <TRNUID>1001                   <!-- Client ID sent in request -->\n          <STATUS>                       <!-- Start status aggregate -->\n            <CODE>0                      <!-- OK -->\n            <SEVERITY>INFO\n          </STATUS>\n          <STMTRS>                       <!-- Begin statement response -->\n            <CURDEF>USD\n            <BANKACCTFROM>               <!-- Identify the account -->\n              <BANKID>121099999          <!-- Routing transit or other FI ID -->\n              <ACCTID>999988             <!-- Account number -->\n              <ACCTTYPE>CHECKING         <!-- Account type -->\n            </BANKACCTFROM>              <!-- End of account ID -->\n            <BANKTRANLIST>               <!-- Begin list of statement trans. -->\n              <DTSTART>19961001          <!-- Start date: Oct. 1, 1996 -->\n              <DTEND>19961028            <!-- End date: Oct. 28, 1996 -->\n              <STMTTRN>                  <!-- First statement transaction -->\n                      <TRNTYPE>CHECK     <!--Check -->\n                      <DTPOSTED>19961004 <!-- Posted on Oct. 4, 1996 -->\n                      <TRNAMT>-200.00    <!-- $200.00 -->\n                      <FITID>00002       <!-- Unique ID -->\n                      <CHECKNUM>1000     <!-- Check number -->\n            </STMTTRN>                   <!-- End statement transaction -->\n            <STMTTRN>                    <!-- Second transaction -->\n                   <TRNTYPE>ATM          <!-- ATM transaction -->\n                      <DTPOSTED>19961020 <!-- Posted on Oct. 20, 1996 -->\n                   <DTUSER>19961020      <!-- User date of Oct. 20, 1996 -->\n                   <TRNAMT>-300.00       <!-- $300.00 -->\n                   <FITID>00003          <!-- Unique ID -->\n            </STMTTRN>                   <!-- End statement transaction -->\n          </BANKTRANLIST>                <!-- End list of statement trans. -->\n          <LEDGERBAL>                    <!-- Ledger balance aggregate -->\n            <BALAMT>200.29               <!-- Bal amount: $200.29 -->\n            <DTASOF>199610291120         <!-- Bal date: 10/29/96, 11:20 am -->\n          </LEDGERBAL>                   <!-- End ledger balance -->\n          <AVAILBAL>                     <!-- Available balance aggregate -->\n            <BALAMT>200.29               <!-- Bal amount: $200.29 -->\n            <DTASOF>199610291120         <!-- Bal date: 10/29/96, 11:20 am -->\n          </AVAILBAL>                    <!-- End available balance -->\n\t</STMTRS>                        <!-- End statement response -->\n     </STMTTRNRS>                        <!-- End of transaction -->\n  </BANKMSGSRSV1>\n</OFX>                                   <!-- End of response data -->\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/ofx_spec201_stmtrs_example.xml",
    "content": "<OFX>                                                   <!-- Begin response data -->\n    <SIGNONMSGSRSV1>\n        <SONRS>                                         <!-- Begin signon -->\n            <STATUS>                                    <!-- Begin status aggregate -->\n                <CODE>0</CODE>\n                <!-- OK -->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <DTSERVER>19991029101003</DTSERVER>\n            <!-- Oct. 29, 1999, 10:10:03 am -->\n            <LANGUAGE>ENG</LANGUAGE>\n            <!-- Language used in response -->\n            <DTPROFUP>19991029101003</DTPROFUP>\n            <!-- Last update to profile-->\n            <DTACCTUP>19991029101003</DTACCTUP>\n            <!-- Last account update -->\n            <FI>                                        <!-- ID of receiving institution -->\n                <ORG>NCH</ORG>\n                <!-- Name of ID owner -->\n                <FID>1001</FID>\n                <!-- Actual ID -->\n            </FI>\n        </SONRS>\n        <!-- End of signon -->\n    </SIGNONMSGSRSV1>\n    <BANKMSGSRSV1>\n        <STMTTRNRS>                                     <!-- Begin response -->\n            <TRNUID>1001</TRNUID>\n            <!-- Client ID sent in request -->\n            <STATUS>                                    <!-- Start status aggregate -->\n                <CODE>0</CODE>\n                <!-- OK -->\n                <SEVERITY>INFO</SEVERITY>\n            </STATUS>\n            <STMTRS>                                    <!-- Begin statement response -->\n                <CURDEF>USD</CURDEF>\n                <BANKACCTFROM>                          <!-- Identify the account -->\n                    <BANKID>121099999</BANKID>\n                    <!-- Routing transit or other FI ID -->\n                    <ACCTID>999988</ACCTID>\n                    <!-- Account number -->\n                    <ACCTTYPE>CHECKING</ACCTTYPE>\n                    <!-- Account type -->\n                </BANKACCTFROM>\n                <!-- End of account ID -->\n                <BANKTRANLIST>                          <!-- Begin list of statement trans. -->\n                    <DTSTART>19991001</DTSTART>\n                    <!-- Start date: Oct. 1, 1999 -->\n                    <DTEND>19991028</DTEND>\n                    <!-- End date: Oct. 28, 1999 -->\n                    <STMTTRN>                           <!-- First statement transaction -->\n                        <TRNTYPE>CHECK</TRNTYPE>\n                        <!--Check -->\n                        <DTPOSTED>19991004</DTPOSTED>\n                        <!-- Posted on Oct. 4, 1999 -->\n                        <TRNAMT>-200.00</TRNAMT>\n                        <!-- $200.00 -->\n                        <FITID>00002</FITID>\n                        <!-- Unique ID -->\n                        <CHECKNUM>1000</CHECKNUM>\n                        <!-- Check number -->\n                    </STMTTRN>\n                    <!-- End statement transaction -->\n                    <STMTTRN>                           <!-- Second transaction -->\n                        <TRNTYPE>ATM</TRNTYPE>\n                        <!-- ATM transaction -->\n                        <DTPOSTED>19991020</DTPOSTED>\n                        <!-- Posted on Oct. 20, 1999 -->\n                        <DTUSER>19991020</DTUSER>\n                        <!-- User date of Oct. 20, 1999 -->\n                        <TRNAMT>-300.00</TRNAMT>\n                        <!-- $300.00 -->\n                        <FITID>00003</FITID>\n                        <!-- Unique ID -->\n                    </STMTTRN>\n                    <!-- End statement transaction -->\n                </BANKTRANLIST>\n                <!-- End list of statement trans. -->\n                <LEDGERBAL>                             <!-- Ledger balance aggregate -->\n                    <BALAMT>200.29</BALAMT>\n                    <!-- Bal amount: $200.29 -->\n                    <DTASOF>199910291120</DTASOF>\n                    <!-- Bal date: 10/29/99, 11:20 am -->\n                </LEDGERBAL>\n                <!-- End ledger balance -->\n                <AVAILBAL>                              <!-- Available balance aggregate -->\n                    <BALAMT>200.29</BALAMT>\n                    <!-- Bal amount: $200.29 -->\n                    <DTASOF>199910291120</DTASOF>\n                    <!-- Bal date: 10/29/99, 11:20 am -->\n                </AVAILBAL>\n                <!-- End available balance -->\n            </STMTRS>\n            <!-- End statement response -->\n        </STMTTRNRS>\n        <!-- End of transaction -->\n    </BANKMSGSRSV1>\n</OFX>                                                  <!-- End of response data -->\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/test_fails.ofx",
    "content": "<OFX>\n<SIGNONMSGSRSV1>\n<SONRS>\n<STATUS>\n<CODE>0</CODE>\n<SEVERITY>INFO</SEVERITY>\n</STATUS>\n<DTSERVER>20181005212513</DTSERVER>\n<LANGUAGE>ENG</LANGUAGE>\n</SONRS>\n</SIGNONMSGSRSV1>\n<BANKMSGSRSV1>\n<STMTTRNRS>\n<TRNUID>99999999999</TRNUID>\n<STATUS>\n<CODE>0</CODE>\n<SEVERITY>INFO</SEVERITY>\n</STATUS>\n<STMTRS>\n<CURDEF>EUR</CURDEF>\n<BANKACCTFROM>\n<BANKID>12345678</BANKID>\n<ACCTID>12345678</ACCTID>\n<ACCTTYPE>CHECKING</ACCTTYPE>\n</BANKACCTFROM>\n<BANKTRANLIST>\n<DTSTART>20180901000000</DTSTART>\n<DTEND>20180930000000</DTEND>\n<STMTTRN>\n<TRNTYPE>OTHER</TRNTYPE>\n<DTPOSTED>20180910</DTPOSTED>\n<TRNAMT>-36.0</TRNAMT>\n<FITID>9999999999999</FITID>\n<NAME>First name</NAME>\n<MEMO>First memo</MEMO>\n<BANKACCTTO>\n<BANKID>9999999999999</BANKID>\n<ACCTID>999992</ACCTID>\n</BANKACCTTO>\n</STMTTRN>\n<STMTTRN>\n<TRNTYPE>OTHER</TRNTYPE>\n<DTPOSTED>20180907</DTPOSTED>\n<TRNAMT>-200.0</TRNAMT>\n<FITID>9999999999999</FITID>\n<NAME>Second & Name</NAME>\n<MEMO>Second Memo</MEMO>\n<BANKACCTTO>\n<BANKID>9999999999999</BANKID>\n<ACCTID>999992</ACCTID>\n</BANKACCTTO>\n</STMTTRN>\n</BANKTRANLIST>\n<LEDGERBAL>\n<BALAMT>+999999.99</BALAMT>\n<DTASOF>19000101</DTASOF>\n</LEDGERBAL>\n<AVAILBAL>\n<BALAMT>+999999.99</BALAMT>\n<DTASOF>19000101</DTASOF>\n</AVAILBAL>\n</STMTRS>\n</STMTTRNRS>\n</BANKMSGSRSV1>\n</OFX>\n"
  },
  {
    "path": "jgnash-tests/src/test/resources/uglyFormat.ofx",
    "content": "\n\n\n\n\n\n\n\n\n\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\n\t\n\t\n\t\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\tOFXHEADER:100\n\t\tDATA:OFXSGML\n\t\tVERSION:102\n\t\tSECURITY:NONE\n\t\tENCODING:USASCII\n\t\tCHARSET:1252\n\t\tCOMPRESSION:NONE\n\t\tOLDFILEUID:NONE\n\t\tNEWFILEUID:NONE\n\t\t<OFX>\n\t\t\t<SIGNONMSGSRSV1>\n\t\t\t\t<SONRS>\n\t\t\t\t\t<STATUS>\n\t\t\t\t\t\t<CODE>0</CODE>\n\t\t\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t\t\t</STATUS>\n\t\t\t\t\t<DTSERVER>\n\t\t\t\t\t\t20170403021159\n\t\t\t\t\t</DTSERVER>\n\t\t\t\t\t<LANGUAGE>ENG</LANGUAGE>\n\t\t\t\t\t<FI>\n\t\t\t\t\t\t<ORG>\n\t\t\t\t\t\t\tDEMOBANK\n\t\t\t\t\t\t</ORG>\n\t\t\t\t\t\t<FID>\n\t\t\t\t\t\t\t1234567\n\t\t\t\t\t\t</FID>\n\t\t\t\t\t</FI>\n\t\t\t\t</SONRS>\n\t\t\t</SIGNONMSGSRSV1>\n\t\t\t<BANKMSGSRSV1>\n\t\t\t\t<STMTTRNRS>\n\t\t\t\t\t<TRNUID>\n\t\t\t\t\t\t456789\n\t\t\t\t\t</TRNUID>\n\t\t\t\t\t<STATUS>\n\t\t\t\t\t\t<CODE>0</CODE>\n\t\t\t\t\t\t<SEVERITY>INFO</SEVERITY>\n\t\t\t\t\t</STATUS>\n\t\t\t\t\t<STMTRS>\n\t\t\t\t\t\t<CURDEF>USD</CURDEF>\n\t\t\t\t\t\t<BANKACCTFROM>\n\t\t\t\t\t\t\t<BANKID>123456789</BANKID>\n\t\t\t\t\t\t\t<ACCTID>123456123456</ACCTID>\n\t\t\t\t\t\t\t<ACCTTYPE>MONEYMRKT</ACCTTYPE>\n\t\t\t\t\t\t</BANKACCTFROM>\n\t\t\t\t\t\t<BANKTRANLIST>\n\t\t\t\t\t\t\t<DTSTART>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t20170315234653\n\t\t\t\t\t\t\t</DTSTART>\n\t\t\t\t\t\t\t<DTEND>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t20170331233411\n\t\t\t\t\t\t\t</DTEND>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t<TRNTYPE>CREDIT</TRNTYPE>\n\t\t\t\t\t\t\t\t\t\t\t<DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t20170331233411\n\t\t\t\t\t\t\t\t\t\t\t</DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t<TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\t\t123.45\n\t\t\t\t\t\t\t\t\t\t\t</TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t<FITID>\n\t\t\t\t\t\t\t\t\t\t123456123456\n\t\t\t\t\t\t\t\t\t\t323\n\t\t\t\t\t\t\t\t\t</FITID>\n\t\t\t\t\t\t\t\t\t<NAME>Interest Paid \n\t\t\t\t\t\t\t\t\t</NAME>\n\t\t\t\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t<TRNTYPE>CREDIT</TRNTYPE>\n\t\t\t\t\t\t\t\t\t\t\t<DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t20170331235707\n\t\t\t\t\t\t\t\t\t\t\t</DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t<TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\t\t1234.56\n\t\t\t\t\t\t\t\t\t\t\t</TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t<FITID>\n\t\t\t\t\t\t\t\t\t\t123456123456\n\t\t\t\t\t\t\t\t\t\t322\n\t\t\t\t\t\t\t\t\t</FITID>\n\t\t\t\t\t\t\t\t\t<NAME>PAYROLL~ Future Amount: 1234.56 ~ Tran: ACHDD\n\t\t\t\t\t\t\t\t\t</NAME>\n\t\t\t\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t<TRNTYPE>DEBIT</TRNTYPE>\n\t\t\t\t\t\t\t\t\t\t\t<DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t20170324024401\n\t\t\t\t\t\t\t\t\t\t\t</DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t<TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\t\t-1234.56\n\t\t\t\t\t\t\t\t\t\t\t</TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t<FITID>\n\t\t\t\t\t\t\t\t\t\t123456123456\n\t\t\t\t\t\t\t\t\t\t316\n\t\t\t\t\t\t\t\t\t</FITID>\n\t\t\t\t\t\t\t\t\t<NAME>Transfer to SOME BANK SAVINGS BANK DDA account ******1234\n\t\t\t\t\t\t\t\t\t</NAME>\n\t\t\t\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t<TRNTYPE>CREDIT</TRNTYPE>\n\t\t\t\t\t\t\t\t\t\t\t<DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t20170315234653\n\t\t\t\t\t\t\t\t\t\t\t</DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t<TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\t\t1234.56\n\t\t\t\t\t\t\t\t\t\t\t</TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t<FITID>\n\t\t\t\t\t\t\t\t\t\t123456123456\n\t\t\t\t\t\t\t\t\t\t313\n\t\t\t\t\t\t\t\t\t</FITID>\n\t\t\t\t\t\t\t\t\t<NAME>PAYROLL~ Future Amount: 1234.56 ~ Tran: ACHDD\n\t\t\t\t\t\t\t\t\t</NAME>\n\t\t\t\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t<STMTTRN>\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t<TRNTYPE>CREDIT</TRNTYPE>\n\t\t\t\t\t\t\t\t\t\t\t<DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t20170315234653\n\t\t\t\t\t\t\t\t\t\t\t</DTPOSTED>\n\t\t\t\t\t\t\t\t\t\t\t<TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\t\t1234.56\n\t\t\t\t\t\t\t\t\t\t\t</TRNAMT>\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t<FITID>\n\t\t\t\t\t\t\t\t\t\t123456123456\n\t\t\t\t\t\t\t\t\t\t312\n\t\t\t\t\t\t\t\t\t</FITID>\n\t\t\t\t\t\t\t\t\t<NAME>PAYROLL~ Future & &amp; \" ' Am'ount: 1234.56 ~ Tran: ACHDD\n\t\t\t\t\t\t\t\t\t</NAME>\n\t\t\t\t\t\t\t\t</STMTTRN>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t</BANKTRANLIST>\n\t\t\t\t\t\t<LEDGERBAL>\n\t\t\t\t\t\t\t<BALAMT>\n\t\t\t\t\t\t\t\t123457.78\n\t\t\t\t\t\t\t</BALAMT>\n\t\t\t\t\t\t\t<DTASOF>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t20170331233411\n\t\t\t\t\t\t\t</DTASOF>\n\t\t\t\t\t\t</LEDGERBAL>\n\t\t\t\t\t\t<AVAILBAL>\n\t\t\t\t\t\t\t<BALAMT>123457.78</BALAMT>\n\t\t\t\t\t\t\t<DTASOF>20170331233411</DTASOF>\n\t\t\t\t\t\t</AVAILBAL>\n\t\t\t\t\t</STMTRS>\n\t\t\t\t</STMTTRNRS>\n\t\t\t</BANKMSGSRSV1>\n\t\t</OFX>\n\t\n"
  },
  {
    "path": "mt940/build.gradle.kts",
    "content": "description = \"mt940 Plugin\"\n\nval javaFXVersion: String by project    // extract JavaFX version from gradle.properties\n\nval junitVersion: String by project\nval junitExtensionsVersion: String by project\nval awaitilityVersion: String by project\n\nplugins {\n    id(\"org.openjfx.javafxplugin\")\n}\n\ndependencies {\n    testImplementation(\"org.junit.jupiter:junit-jupiter-api:$junitVersion\")\n    testImplementation(\"org.junit.jupiter:junit-jupiter-params:$junitVersion\")\n    testRuntimeOnly(\"org.junit.jupiter:junit-jupiter-engine:$junitVersion\")\n    testImplementation(\"io.github.glytching:junit-extensions:$junitExtensionsVersion\")\n    testImplementation(\"org.awaitility:awaitility:$awaitilityVersion\")\n\n    implementation(project(\":jgnash-resources\"))\n    implementation(project(\":jgnash-core\"))\n    implementation(project(\":jgnash-fx\"))\n    implementation(project(\":jgnash-plugin\"))\n    implementation(project(\":jgnash-convert\"))\n}\n\njavafx {\n    version = javaFXVersion\n    modules(\"javafx.controls\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n\n    // we want display the following test events\n    testLogging {\n        events(\"PASSED\", \"STARTED\", \"FAILED\", \"SKIPPED\")\n        showStandardStreams = true\n    }\n}\n\ntasks.jar {\n    // Keep jar clean:\n    exclude (\"META-INF/*.SF\", \"META-INF/*.DSA\", \"META-INF/*.RSA\", \"META-INF/*.MF\")\n\n    // required by the plugin interface\n    manifest {\n        attributes(mapOf(\"Plugin-Activator\" to \"net.bzzt.swift.mt940.Mt940Plugin\", \"Plugin-Version\" to \"2.25\"))\n    }\n}\n"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/ImportMt940FxAction.java",
    "content": "/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage net.bzzt.swift.mt940;\n\nimport java.io.File;\nimport java.io.LineNumberReader;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.util.List;\nimport java.util.ResourceBundle;\nimport java.util.prefs.Preferences;\n\nimport javafx.concurrent.Task;\nimport javafx.stage.FileChooser;\n\nimport jgnash.convert.importat.GenericImport;\nimport jgnash.convert.importat.ImportBank;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.engine.Account;\nimport jgnash.engine.Engine;\nimport jgnash.engine.EngineFactory;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.StaticUIMethods;\nimport jgnash.uifx.control.wizard.WizardDialogController;\nimport jgnash.uifx.views.main.MainView;\nimport jgnash.uifx.wizard.imports.ImportWizard;\nimport jgnash.util.FileMagic;\n\nimport net.bzzt.swift.mt940.exporter.Mt940Exporter;\nimport net.bzzt.swift.mt940.parser.Mt940Parser;\n\n/**\n * Utility class to import a Mt940 file.\n *\n * @author Craig Cavanaugh\n */\nclass ImportMt940FxAction {\n\n    private static final String LAST_DIR = \"importDir\";\n\n    private ImportMt940FxAction() {\n        // Utility class\n    }\n\n    static void showAndWait() {\n        final ResourceBundle resources = ResourceUtils.getBundle();\n\n        final Engine engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\n        if (engine == null || engine.getRootAccount().getChildCount() == 0) {\n            StaticUIMethods.displayError(resources.getString(\"Message.Error.CreateBasicAccounts\"));\n            return;\n        }\n\n        final FileChooser fileChooser = configureFileChooser();\n        fileChooser.setTitle(resources.getString(\"Title.SelFile\"));\n      \n\t\tfinal File file = fileChooser.showOpenDialog(MainView.getPrimaryStage());\n\n        if (file != null) {\n            Preferences pref = Preferences.userNodeForPackage(ImportMt940FxAction.class);\n            pref.put(LAST_DIR, file.getParentFile().getAbsolutePath());\n\n            new Thread(new ImportTask(file)).start();\n        }\n    }\n\n    private static FileChooser configureFileChooser() {\n        final Preferences pref = Preferences.userNodeForPackage(ImportMt940FxAction.class);\n        final FileChooser fileChooser = new FileChooser();\n\n        final File initialDirectory = new File(pref.get(LAST_DIR, System.getProperty(\"user.home\")));\n\n        // Protect against an IllegalArgumentException\n        if (initialDirectory.isDirectory()) {\n            fileChooser.setInitialDirectory(initialDirectory);\n        }\n\n        fileChooser.getExtensionFilters().addAll(\n                new FileChooser.ExtensionFilter(\"All Files (*.*)\", \"*.*\")\n        );\n\n        return fileChooser;\n    }\n\n    private static class ImportTask extends Task<ImportBank<ImportTransaction>> {\n\n        private final File file;\n\n        ImportTask(final File file) {\n            this.file = file;\n\n            setOnSucceeded(event -> onSuccess());\n        }\n\n        @Override\n        protected ImportBank<ImportTransaction> call() throws Exception {\n            final Charset charset = FileMagic.detectCharset(file.getAbsolutePath());\n\n            try (final LineNumberReader reader = new LineNumberReader(Files.newBufferedReader(file.toPath(), charset))) {\n                final Mt940File parsedFile = Mt940Parser.parse(reader);\n                return Mt940Exporter.convert(parsedFile);\n            }\n        }\n\n        private void onSuccess() {\n            ImportBank<ImportTransaction> bank = getValue();\n\n            final ImportWizard importWizard = new ImportWizard();\n\n            WizardDialogController<ImportWizard.Settings> wizardDialogController\n                    = importWizard.wizardControllerProperty().get();\n\n            wizardDialogController.setSetting(ImportWizard.Settings.BANK, bank);\n\n            importWizard.showAndWait();\n\n            if (wizardDialogController.validProperty().get()) {\n                @SuppressWarnings(\"unchecked\")\n                final List<ImportTransaction> transactions =\n                        (List<ImportTransaction>) wizardDialogController.getSetting(ImportWizard.Settings.TRANSACTIONS);\n\n                final Account account = (Account) wizardDialogController.getSetting(ImportWizard.Settings.ACCOUNT);\n\n                // import threads in the background\n                final ImportTransactionsTask importTransactionsTask = new ImportTransactionsTask(account, transactions);\n\n                new Thread(importTransactionsTask).start();\n\n                StaticUIMethods.displayTaskProgress(importTransactionsTask);\n            }\n        }\n    }\n\n    private static class ImportTransactionsTask extends Task<Void> {\n\n        private final Account account;\n        private final List<ImportTransaction> transactions;\n\n        ImportTransactionsTask(final Account account, final List<ImportTransaction> transactions) {\n            this.account = account;\n            this.transactions = transactions;\n        }\n\n        @Override\n        public Void call() {\n            updateMessage(ResourceUtils.getString(\"Message.PleaseWait\"));\n            updateProgress(-1, Long.MAX_VALUE);\n\n            /* Import the transactions */\n            GenericImport.importTransactions(transactions, account);\n\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/Mt940Entry.java",
    "content": "/*\n * Copyright (C) 2008 Arnout Engelen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage net.bzzt.swift.mt940;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.time.LocalDate;\n\npublic class Mt940Entry {\n    // 'Credit', in mt940, means money was transferred \n    // to the current account.\n    public enum SollHabenKennung {\n        CREDIT,\n        DEBIT,\n        //STORNO_DEBIT,\n        //STORNO_CREDIT\n    }\n\n    private LocalDate valutaDatum;  // date\n\n    private SollHabenKennung sollHabenKennung;\n\n    private BigDecimal betrag;  // amount\n\n    private String mehrzweckfeld;   // multi purpose field\n\n    private String kontobezeichnung;\n\n    public LocalDate getValutaDatum() {\n        return valutaDatum;\n    }\n\n    public void setValutaDatum(final LocalDate valutaDatum) {\n        this.valutaDatum = valutaDatum;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder(\"At \").append(valutaDatum);\n        if (kontobezeichnung != null) {\n            sb.append(\" (\");\n            sb.append(kontobezeichnung);\n            sb.append(\")\");\n        }\n        sb.append(\", \");\n        sb.append(sollHabenKennung);\n        sb.append(\": \");\n        if (betrag != null) {\n            // Set scale to 2 digits and round if necessary, then to plain string for nicer output.\n            sb.append(betrag.setScale(2, RoundingMode.HALF_EVEN).toPlainString());\n        } else {\n            sb.append(\"-\");\n        }\n        sb.append(\" for \");\n        sb.append(mehrzweckfeld);\n        return sb.toString();\n    }\n\n    public SollHabenKennung getSollHabenKennung() {\n        return sollHabenKennung;\n    }\n\n    public void setSollHabenKennung(SollHabenKennung sollHabenKennung) {\n        this.sollHabenKennung = sollHabenKennung;\n    }\n\n    public String getMehrzweckfeld() {\n        return mehrzweckfeld;\n    }\n\n//    public void setMehrzweckfeld(String mehrzweckfeld) {\n//        this.mehrzweckfeld = mehrzweckfeld;\n//    }\n\n    public BigDecimal getBetrag() {\n        return betrag;\n    }\n\n    public void setBetrag(final BigDecimal betrag) {\n        this.betrag = betrag;\n    }\n\n    public void addToMehrzweckfeld(final String string) {\n        if (mehrzweckfeld == null || mehrzweckfeld.trim().isEmpty()) {\n            mehrzweckfeld = string;\n        } else {\n            mehrzweckfeld += \" \";\n            mehrzweckfeld += string;\n        }\n    }\n\n    public void setKontobezeichnung(final String kontobezeichnung) {\n        this.kontobezeichnung = kontobezeichnung;\n    }\n\n    public String getKontobezeichnung() {\n        return kontobezeichnung;\n    }\n}\n"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/Mt940File.java",
    "content": "package net.bzzt.swift.mt940;\n/*\n * Copyright (C) 2008 Arnout Engelen\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic class Mt940File {\n\tprivate List<Mt940Record> records = new ArrayList<>();\n\n\tpublic List<Mt940Record> getRecords() {\n\t\treturn records;\n\t}\n\n\tpublic void setRecords(List<Mt940Record> records) {\n\t\tthis.records = records;\n\t}\n\n\tList<Mt940Entry> getEntries() {\n\t\tList<Mt940Entry> retval = new ArrayList<>();\n\t\tfor (Mt940Record record : getRecords())\n\t\t{\n\t\t\tretval.addAll(record.getEntries());\n\t\t}\n\t\treturn retval;\n\t}\n}\n"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/Mt940Plugin.java",
    "content": "/*\n * Copyright (C) 2008 Arnout Engelen\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage net.bzzt.swift.mt940;\n\nimport javafx.scene.control.Menu;\nimport javafx.scene.control.MenuBar;\nimport javafx.scene.control.MenuItem;\n\nimport jgnash.engine.message.Message;\nimport jgnash.engine.message.MessageBus;\nimport jgnash.engine.message.MessageChannel;\nimport jgnash.engine.message.MessageListener;\nimport jgnash.plugin.FxPlugin;\nimport jgnash.resource.util.ResourceUtils;\nimport jgnash.uifx.util.JavaFXUtils;\nimport jgnash.uifx.views.main.MainView;\n\npublic class Mt940Plugin implements FxPlugin {\n\n    private static final int MENU_INDEX = 2;\n\n    @Override\n    public String getName() {\n        return \"MT940 Import\";\n    }\n\n    @Override\n    public void start(final PluginPlatform pluginPlatform) {\n        if (pluginPlatform == PluginPlatform.Fx) {\n            installFxMenu();\n        }\n    }\n\n    private static void installFxMenu() {\n        final MenuBar menuBar = MainView.getInstance().getMenuBar();\n\n        menuBar.getMenus().stream().filter(menu -> \"fileMenu\".equals(menu.getId())).forEach(menu -> menu.getItems()\n                .stream().filter(menuItem -> menuItem instanceof Menu)\n                .filter(menuItem -> \"importMenu\".equals(menuItem.getId())).forEach(menuItem -> {\n\n            final MenuItemEx importMenuItem = new MenuItemEx(ResourceUtils.getString(\"Menu.ImportMt940.Name\"));\n\n            importMenuItem.setOnAction(event -> ImportMt940FxAction.showAndWait());\n\n            ((Menu) menuItem).getItems().add(MENU_INDEX, importMenuItem);\n        }));\n    }\n\n    private static class MenuItemEx extends MenuItem implements MessageListener {\n\n        MenuItemEx(final String text) {\n            super(text);\n            MessageBus.getInstance().registerListener(this, MessageChannel.SYSTEM);\n            disableProperty().setValue(true);\n        }\n\n        @Override\n        public void messagePosted(final Message message) {\n            JavaFXUtils.runLater(() -> {\n                switch (message.getEvent()) {\n                    case FILE_LOAD_SUCCESS:\n                        disableProperty().setValue(false);\n                        break;\n                    case FILE_CLOSING:\n                        disableProperty().setValue(true);\n                        break;\n                    default:\n                        break;\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/Mt940Record.java",
    "content": "/*\n * Copyright (C) 2008 Arnout Engelen\n * \n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage net.bzzt.swift.mt940;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Mt940Record {\n\tprivate List<Mt940Entry> entries = new ArrayList<>();\n\n\tpublic List<Mt940Entry> getEntries() {\n\t\treturn entries;\n\t}\n\n\tpublic void setEntries(List<Mt940Entry> entries) {\n\t\tthis.entries = entries;\n\t}\n}\n"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/exporter/Mt940Exporter.java",
    "content": "/*\n * Copyright (C) 2008 Arnout Engelen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage net.bzzt.swift.mt940.exporter;\n\nimport java.math.BigDecimal;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport jgnash.convert.importat.ImportBank;\nimport jgnash.convert.importat.ImportTransaction;\n\nimport net.bzzt.swift.mt940.Mt940Entry;\nimport net.bzzt.swift.mt940.Mt940Entry.SollHabenKennung;\nimport net.bzzt.swift.mt940.Mt940File;\nimport net.bzzt.swift.mt940.Mt940Record;\n\n/**\n * The Mt940 Exporter converts a parsed Mt940File to jGnash-specific\n * Double-entry Transaction objects.\n *\n * @author arnouten\n */\npublic class Mt940Exporter {\n    private Mt940Exporter() {\n    }\n\n    public static ImportBank<ImportTransaction> convert(Mt940File file) {\n        final ImportBank<ImportTransaction> importBank = new ImportBank<>();\n\n        importBank.setTransactions(convertTransactions(file));\n\n        return importBank;\n    }\n\n    /**\n     * Convert an entire Mt940File to Transactions\n     *\n     * @param file file to import\n     * @return list of import transactions\n     */\n    private static List<ImportTransaction> convertTransactions(final Mt940File file) {\n        List<ImportTransaction> retVal = new ArrayList<>();\n        for (Mt940Record record : file.getRecords()) {\n            retVal.addAll(record.getEntries().stream().map(Mt940Exporter::convert).collect(Collectors.toList()));\n        }\n        return retVal;\n    }\n\n    /**\n     * Convert a single Mt940-entry to a jGnash-Transaction\n     *\n     * @param entry Mt940Entry to convert\n     * @return new import transaction\n     */\n    private static ImportTransaction convert(Mt940Entry entry) {\n        BigDecimal amount;\n\n        if (entry.getSollHabenKennung() == SollHabenKennung.CREDIT) {\n            // The bank account is credited, so we gained income\n            amount = entry.getBetrag();\n        } else if (entry.getSollHabenKennung() == SollHabenKennung.DEBIT) {\n            // The bank account is debited, so we made expenses\n            // withdrawals have a negative 'amount'\n            amount = BigDecimal.valueOf(0L).subtract(entry.getBetrag());\n        } else {\n            throw new UnsupportedOperationException(\"SollHabenKennung \" + entry.getSollHabenKennung() + \" not supported\");\n        }\n        \n        ImportTransaction tran = new ImportTransaction();\n        tran.setAmount(amount);\n        tran.setDatePosted(entry.getValutaDatum());\n        tran.setMemo(entry.getMehrzweckfeld());\n        tran.setAccount(null);\n\n        return tran;\n    }\n}"
  },
  {
    "path": "mt940/src/main/java/net/bzzt/swift/mt940/parser/Mt940Parser.java",
    "content": "/*\n * Copyright (C) 2008 Arnout Engelen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage net.bzzt.swift.mt940.parser;\n\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.math.BigDecimal;\nimport java.time.LocalDate;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeParseException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport net.bzzt.swift.mt940.Mt940Entry;\nimport net.bzzt.swift.mt940.Mt940Entry.SollHabenKennung;\nimport net.bzzt.swift.mt940.Mt940File;\nimport net.bzzt.swift.mt940.Mt940Record;\n\n/**\n * MT940 Parser.\n *\n * @author Arnout Engelen\n * @author Miroslav Holubec\n * @author Craig Cavanaugh\n */\npublic class Mt940Parser {\n\n    private static final String PREFIX_MERHZWECKFELD = \":86:\";\n    private static final String PREFIX_KONTOBEZEICHNUNG = \":25:\";\n    private static final String PREFIX_ENTRY_START = \":61:\";\n\n    /**\n     * Parse the Mt940-file. Mt940 records are delimited by '-'.\n     *\n     * @param reader reader\n     * @return Mt940File instance\n     * @throws IOException    An IO exception occurred\n     * @throws DateTimeParseException parse error occurred reading text\n     */\n    public static Mt940File parse(final LineNumberReader reader) throws IOException, DateTimeParseException {\n        final Mt940File mt940File = new Mt940File();\n\n        List<String> recordLines = new ArrayList<>();\n\n        String currentLine = reader.readLine();\n        while (currentLine != null) {\n            if (currentLine.startsWith(\"-\")) {\n                // Parse this record and add it to the file\n                mt940File.getRecords().add(parseRecord(recordLines));\n\n                // Clear recordLines to start on the next record\n                recordLines = new ArrayList<>();\n            } else {\n                recordLines.add(currentLine);\n            }\n            currentLine = reader.readLine();\n        }\n\n        // A file might not end with a trailing '-' (e.g. from Rabobank):\n        mt940File.getRecords().add(parseRecord(recordLines));\n\n        return mt940File;\n    }\n\n    /**\n     * An mt940-record first has a couple of 'header' lines that do not\n     * start with a ':'.\n     * <p>\n     * After that, a line that doesn't start with a ':' is assumed to\n     * belong to the previous 'real' line.\n     *\n     * @param recordLines list of records\n     * @return List of strings that have been correctly merged\n     */\n    private static List<String> mergeLines(final List<String> recordLines) {\n        List<String> retVal = new ArrayList<>();\n\n        StringBuilder currentString = new StringBuilder();\n\n        boolean inMessage = false;\n        for (String string : recordLines) {\n            // Are we in the message proper, after the\n            // header?\n            if (inMessage) {\n                if (string.startsWith(\":\")) {\n                    retVal.add(currentString.toString());\n                    currentString = new StringBuilder();\n                }\n                currentString.append(string);\n            } else {\n                if (string.startsWith(\":\")) {\n                    // then we're past the header\n                    inMessage = true;\n                    currentString = new StringBuilder(string);\n                } else {\n                    // add a line of the header\n                    retVal.add(string);\n                }\n            }\n        }\n        return retVal;\n    }\n\n    /**\n     * An mt940-record consists of some general lines and a couple\n     * of entries consisting of a :61: and a :86:-section.\n     *\n     * @param recordLines the List of MT940 records to parse\n     * @return and generate Mt940 Record\n     * @throws DateTimeParseException parse error occurred reading text\n     */\n    private static Mt940Record parseRecord(final List<String> recordLines) throws DateTimeParseException {\n        Mt940Record mt940Record = new Mt940Record();\n\n        // Merge 'lines' that span multiple actual lines.\n        List<String> mergedLines = mergeLines(recordLines);\n\n        Mt940Entry currentEntry = null;\n        String currentAccount = null;\n        for (String line : mergedLines) {\n            if (line.startsWith(PREFIX_KONTOBEZEICHNUNG)) {\n                currentAccount = line.substring(PREFIX_KONTOBEZEICHNUNG.length());\n            }\n\n            if (line.startsWith(PREFIX_ENTRY_START)) {\n                currentEntry = nextEntry(mt940Record.getEntries(), currentEntry, currentAccount);\n\n                line = line.substring(PREFIX_ENTRY_START.length());\n                line = parseDatumJJMMTT(currentEntry, line);\n                // for now don't handle the buchungsdatum. It is optional.\n                if (startsWithBuchungsDatum(line)) {\n                    line = line.substring(4);\n                }\n                // for now only support C and D, not RC and RD\n                line = parseSollHabenKennung(currentEntry, line);\n                line = parseBetrag(currentEntry, line);\n            }\n            if (line.startsWith(PREFIX_MERHZWECKFELD) && currentEntry != null) {\n                currentEntry.addToMehrzweckfeld(line.substring(PREFIX_MERHZWECKFELD.length()));\n            }\n        }\n\n        // add the last one:\n        nextEntry(mt940Record.getEntries(), currentEntry, currentAccount);\n\n        return mt940Record;\n    }\n\n    /**\n     * Adds the current entry to the result as a side-effect, if available.\n     *\n     * @param entries       entry list\n     * @param previousEntry entry to add if not null;\n     * @return new working {@code Mt940Entry}\n     */\n    private static Mt940Entry nextEntry(List<Mt940Entry> entries, Mt940Entry previousEntry, String currentAccount) {\n        if (previousEntry != null) {\n            entries.add(previousEntry);\n        }\n\n        Mt940Entry entry = new Mt940Entry();\n        entry.setKontobezeichnung(currentAccount);\n        return entry;\n    }\n\n    /**\n     * BuchungsDatum is a 4-character optional field - but how can we check whether it was included.\n     * <p>\n     * The field is directly followed by the mandatory 'soll/haben-kennung' character, so\n     * we assume that when the string starts with a digit that's probably the buchungsdatum\n     *\n     * @param line line to check for BuchungsDatum\n     * @return true if found\n     */\n    private static boolean startsWithBuchungsDatum(final String line) {\n        return line != null && line.matches(\"^\\\\d.*\");\n    }\n\n    /**\n     * Parse the value, put it into the entry.\n     *\n     * @param currentEntry working {@code Mt940Entry}\n     * @param line line to parse decimal value from\n     * @return the rest of the string to be parsed\n     */\n    private static String parseBetrag(final Mt940Entry currentEntry, final String line) {\n        int endIndex = line.indexOf('N');\n        if (endIndex < 0) {\n            endIndex = line.indexOf('F');\n        }\n\n        String decimal = line.substring(0, endIndex);\n        decimal = decimal.replaceAll(\",\", \".\");\n\n        // According to the MT940 Standard the amount (field :61:) could start with the last character of the currency\n        // e.g. R for EUR\n        // See: https://www.kontopruef.de/mt940s.shtml\n        // This code removes any character which is not a decimal or point.\n        decimal = decimal.replaceAll(\"[^\\\\d.]\", \"\");\n\n        currentEntry.setBetrag(new BigDecimal(decimal));\n\n        return line.substring(endIndex);\n    }\n\n    /**\n     * Parse the debit/credit value, put it into the entry.\n     *\n     * @param currentEntry working {@code Mt940Entry}\n     * @param string credit / debit line to parse\n     * @return the rest of the string to be parsed\n     */\n    private static String parseSollHabenKennung(final Mt940Entry currentEntry, final String string) {\n        String s = string;\n\n        if (string.startsWith(\"D\")) {\n            currentEntry.setSollHabenKennung(SollHabenKennung.DEBIT);\n            s = string.substring(1);\n        } else if (string.startsWith(\"C\")) {\n            currentEntry.setSollHabenKennung(SollHabenKennung.CREDIT);\n            s = string.substring(1);\n        } else {\n            throw new UnsupportedOperationException(\"soll-haben-kennung \" + s + \" not yet supported\");\n        }\n        return s;\n    }\n\n    /**\n     * Parse the formatted date, put it into the entry.\n     *\n     * @param currentEntry working {@code Mt940Entry}\n     * @param string string to parse date from\n     * @return the rest of the string to be parsed\n     * @throws DateTimeParseException thrown if date format is bad\n     */\n    private static String parseDatumJJMMTT(final Mt940Entry currentEntry, final String string) throws DateTimeParseException {\n        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(\"yyMMdd\");\n\n        final String date = string.substring(0, 6);\n\n        currentEntry.setValutaDatum(LocalDate.from(dateTimeFormatter.parse(date)));\n\n        return string.substring(6);\n    }\n}\n"
  },
  {
    "path": "mt940/src/test/java/net/bzzt/swift/mt940/Mt940Test.java",
    "content": "/*\n * Copyright (C) 2008 Arnout Engelen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\npackage net.bzzt.swift.mt940;\n\nimport jgnash.convert.importat.ImportBank;\nimport jgnash.convert.importat.ImportTransaction;\nimport jgnash.util.FileMagic;\n\nimport net.bzzt.swift.mt940.exporter.Mt940Exporter;\nimport net.bzzt.swift.mt940.parser.Mt940Parser;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.LineNumberReader;\nimport java.nio.charset.Charset;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\n\n/**\n * Tests for SWIFT Mt940 importing\n * \n * @author arnouten\n */\nclass Mt940Test {\n\n\t/**\n\t * Rudimentary test for mt940 importing: creates a parser, parses a given mt940\n\t * file and checks that indeed, 18 transactions have been generated.\n\t * \n\t * Then, converts the parsed file to an ImportBank and verifies that there's\n\t * still 18 transactions.\n\t * \n\t * @throws Exception throws assert exception\n\t */\n\t@Test\n\tvoid testMt940() throws Exception {\n\t\tint nTransactions = 18;\n\n\t\tInputStream inputStream = this.getClass().getResourceAsStream(\"/bank1.STA\");\n\t\tfinal Charset charset = FileMagic.detectCharset(inputStream);\n\n\t\t// reopen the input stream, FileMagic closed it\n\t\tinputStream = this.getClass().getResourceAsStream(\"/bank1.STA\");\n\n\t\ttry (LineNumberReader reader = new LineNumberReader(new InputStreamReader(inputStream, charset))) {\n\t\t\tMt940File file = Mt940Parser.parse(reader);\n\n\t\t\tassertEquals(nTransactions, file.getEntries().size());\n\n\t\t\tImportBank<ImportTransaction> bank = Mt940Exporter.convert(file);\n\t\t\tassertEquals(nTransactions, bank.getTransactions().size());\n\t\t}\n\t}\n\n\t/**\n\t * Test that ckontobezeichnung get reads in\n\t * \n\t * @throws Exception throws assert exception\n\t */\n\t@Test\n\tvoid testMt940Kontobezeichnung() throws Exception {\n\n\t\tInputStream inputStream = this.getClass().getResourceAsStream(\"/bank1.STA\");\n\t\tfinal Charset charset = FileMagic.detectCharset(inputStream);\n\n\t\t// reopen the input stream, FileMagic closed it\n\t\tinputStream = this.getClass().getResourceAsStream(\"/bank1.STA\");\n\n\t\ttry (final LineNumberReader reader = new LineNumberReader(new InputStreamReader(inputStream, charset))) {\n\t\t\tMt940File file = Mt940Parser.parse(reader);\n\t\t\tList<Mt940Entry> entries = file.getEntries();\n\t\t\tassertFalse(entries.isEmpty());\n\t\t\tfor (Mt940Entry mt940Entry : entries) {\n\t\t\t\tassertEquals(\"531848396\", mt940Entry.getKontobezeichnung());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Test that ckontobezeichnung get reads in\n\t * \n\t * @throws Exception throws assert exception\n\t */\n\t@Test\n\tvoid testMt940KontobezeichnungRabobank() throws Exception {\n\n\t\tInputStream inputStream = this.getClass().getResourceAsStream(\"/rabobank.swi\");\n\t\tfinal Charset charset = FileMagic.detectCharset(inputStream);\n\n\t\t// reopen the input stream, FileMagic closed it\n\t\tinputStream = this.getClass().getResourceAsStream(\"/rabobank.swi\");\n\n\t\ttry (LineNumberReader reader = new LineNumberReader(new InputStreamReader(inputStream, charset))) {\n\t\t\tMt940File file = Mt940Parser.parse(reader);\n\t\t\tList<Mt940Entry> entries = file.getEntries();\n\t\t\tassertFalse(entries.isEmpty());\n\t\t\tfor (Mt940Entry mt940Entry : entries) {\n\t\t\t\tassertEquals(\"3xxxxxx.013EUR\", mt940Entry.getKontobezeichnung().trim());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Test that ckontobezeichnung get reads in\n\t * \n\t * @throws Exception throws assert exception\n\t */\n\t@Test\n\tvoid testMt940KontobezeichnungMulticash() throws Exception {\n\n\t\tInputStream inputStream = this.getClass().getResourceAsStream(\"/multiaccounts.sta\");\n\t\tfinal Charset charset = FileMagic.detectCharset(inputStream);\n\n\t\t// reopen the input stream, FileMagic closed it\n\t\tinputStream = this.getClass().getResourceAsStream(\"/multiaccounts.sta\");\n\n\t\ttry (final LineNumberReader reader = new LineNumberReader(new InputStreamReader(inputStream, charset))) {\n\t\t\tMt940File file = Mt940Parser.parse(reader);\n\t\t\tList<Mt940Entry> entries = file.getEntries();\n\t\t\tassertEquals(2, entries.size());\n\t\t\tassertEquals(\"531848396\", entries.get(0).getKontobezeichnung());\n\t\t\tassertEquals(\"3xxxxxx.013EUR\", entries.get(1).getKontobezeichnung().trim());\n\t\t}\n\t}\n\n\t/**\n\t * Test parsing an (anonimized) mt940 file as produced by the Rabobank online\n\t * bank\n\t *\n\t * @throws IOException thrown if an IO exception occurs while reading the file\n\t */\n\t@Test\n\tvoid testMt940Rabobank() throws IOException {\n\t\tint nTransactions = 6;\n\n\t\tInputStream inputStream = this.getClass().getResourceAsStream(\"/rabobank.swi\");\n\t\tfinal Charset charset = FileMagic.detectCharset(inputStream);\n\n\t\t// reopen the input stream, FileMagic closed it\n\t\tinputStream = this.getClass().getResourceAsStream(\"/rabobank.swi\");\n\n\t\ttry (final LineNumberReader reader = new LineNumberReader(new InputStreamReader(inputStream, charset))) {\n\t\t\tMt940File file = Mt940Parser.parse(reader);\n\t\t\tassertEquals(nTransactions, file.getEntries().size());\n\n\t\t\tImportBank<ImportTransaction> bank = Mt940Exporter.convert(file);\n\t\t\tassertEquals(nTransactions, bank.getTransactions().size());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "mt940/src/test/resources/bank1.STA",
    "content": "ABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:33001/1\n:60F:C071122EUR4757,92\n:61:0711261126D3,15N426NONREF\n:86:BEA   NR 5VTT02   26.11.07/17.39\n5824 AH TO GO DEVE DEVEN,PAS555\n:61:0711241126D62,5N192NONREF\n:86: 57.38.58.128 QHARMONY\n1E TERMIJN CONCERTREIS\n:61:0711241126D20,N361NONREF\n:86:GEA   NR S1W246   24.11.07/22.31\nDAALSEWEG 200 NIJMEGEN,PAS555\n:62F:C071126EUR4672,27\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:33101/1\n:60F:C071126EUR4672,27\n:61:0711271127D20,N445NONREF\n:86:GEA   NR 000909   27.11.07/18.41\nPOSTBANK NIJMEGEN GWK,PAS555\n:62F:C071127EUR4652,27\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:33201/1\n:60F:C071127EUR4652,27\n:61:0711281128D825,N426NONREF\n:86:BEA   NR 44GA01   28.11.07/14.54\nDE SAXOFOONWINKEL DEVENT,PAS555\n:62F:C071128EUR3827,27\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:33301/1\n:60F:C071128EUR3827,27\n:61:0711291129D30,N445NONREF\n:86:GEA   NR 001732   29.11.07/19.10\nPOSTBANK NIJMEGEN GWK,PAS555\n:61:0711291129D22,3N426NONREF\n:86:BEA   NR 5VTT01   29.11.07/17.53\n5824 AH TO GO DEVE DEVEN,PAS555\n:62F:C071129EUR3774,97\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:33401/1\n:60F:C071129EUR3774,97\n:61:0711301130D32,7N426NONREF\n:86:BEA   NR EH7001   30.11.07/12.14\nALBERT HEIJN 1358 DEVENT,PAS555\n:62F:C071130EUR3742,27\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:33701/1\n:60F:C071130EUR3742,27\n:61:0712011203D9,25N192NONREF\n:86: 73.39.59.555\nT-MOBILE NETHERLANDS BV\nBETALINGSKENM.  500403694285\n901061372890\n:61:0712011203D9,95N426NONREF\n:86:BEA   NR 4RVF02   01.12.07/12.27\nDEKKER NIJMEGEN,PAS555\n:61:0712011203D51,N426NONREF\n:86:BEA   NR 863401   01.12.07/11.35\nMUZIEKH. SCHREEVEN NIJME,PAS555\n:62F:C071203EUR3672,07\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:34101/1\n:60F:C071203EUR3672,07\n:61:0712061207D26,18N192NONREF\n:86:GIRO  8688111 XEL MEDIA BV\nFACTUUR 25338 / KLANT 62239\nXEL.NL\nXEL MEDIA BV UTRECHT\n:62F:C071207EUR3645,89\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:34501/1\n:60F:C071207EUR3645,89\n:61:0712111211D30,N445NONREF\n:86:GEA   NR 000909   11.12.07/19.11\nPOSTBANK NIJMEGEN GWK,PAS555\n:62F:C071211EUR3615,89\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:34601/1\n:60F:C071211EUR3615,89\n:61:0712121212D30,N445NONREF\n:86:GEA   NR 001732   12.12.07/18.38\nPOSTBANK NIJMEGEN GWK,PAS555\n:62F:C071212EUR3585,89\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:35101/1\n:60F:C071212EUR3585,89\n:61:0712151217D29,74N426NONREF\n:86:BEA   NR YU5303   15.12.07/15.19\n136 COOP CODIS 136 NIJME,PAS555\n:62F:C071217EUR3556,15\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:35301/1\n:60F:C071217EUR3556,15\n:61:0712191219D20,N445NONREF\n:86:GEA   NR 001732   19.12.07/18.51\nPOSTBANK NIJMEGEN GWK,PAS555\n:62F:C071219EUR3536,15\n-\nABNANL2A\n940\nABNANL2A\n:20:ABN AMRO BANK NV\n:25:531848396\n:28:35401/1\n:60F:C071219EUR3536,15\n:61:0712211220C46,N196NONREF\n:86:GIRO  2445588 BELASTINGDIENST\nMAAND JAN. NR. 155061288T800013\nZORGTOESLAG 2008  *ENGELEN\n*   BELASTINGDIENST GVS UITBET\nALING.\n:61:0712211220C1716,27N196NONREF\n:86: 10.52.65.896 TOPICUS ZORG B.V.\nBRINKPOORTSTRAAT 11\n7411 HR  DEVENTER\n15/10002778 Y2007*12*15*1 XX15 S\nALARIS DEC 07X ENGELEN*STANDAARD\nBANKREKENING*Y2007*12\n:62F:C071220EUR5298,42\n-\n"
  },
  {
    "path": "mt940/src/test/resources/multiaccounts.sta",
    "content": ":20:ABN AMRO BANK NV\n:25:531848396\n:28:33001/1\n:60F:C071122EUR4757,92\n:61:0711261126D3,15N426NONREF\n:86:BEA   NR 5VTT02   26.11.07/17.39\n5824 AH TO GO DEVE DEVEN,PAS555\n\n:20:940xxxxxx3\n:25:3xxxxxx.013EUR\n:28:00000/00\n:60F:C101231EUR000000005360,80\n:61:110103C000000000015,00N122P000xxxxxx      Jxxxxxxxxxxxxxxxx\n:86:MAANDELIJKSE CONTRIBUTIE xxxxxxxx\n:86:TRANSACTIEDATUM* 01-01-2011\n"
  },
  {
    "path": "mt940/src/test/resources/rabobank.swi",
    "content": ":20:940xxxxxx3\r\n:25:3xxxxxx.013EUR                     \r\n:28:00000/00\r\n:60F:C101231EUR000000005360,80\r\n:61:110103C000000000015,00N122P000xxxxxx      Jxxxxxxxxxxxxxxxx                 \r\n:86:MAANDELIJKSE CONTRIBUTIE xxxxxxxx                                \r\n:86:TRANSACTIEDATUM* 01-01-2011                                      \r\n:61:110103C000000000015,00N122P001xxxxxx      Wxxxxxxxxxxxxxxxxenburg           \r\n:86:CONTRIBUTIE                                                      \r\n:86:TRANSACTIEDATUM* 01-01-2011                                      \r\n:61:110103C000000000015,00N122P001xxxxxx      Hxxxxxxxxxxxxxxxx                 \r\n:86:CONTRIBUTIE BBSA                                                 \r\n:86:TRANSACTIEDATUM* 01-01-2011                                      \r\n:61:110103C000000000015,00N122P003xxxxxx      Axxxxxxxxxxxxxxxx en M v          \r\n:86:CONTRIBUTIE                                                      \r\n:86:TRANSACTIEDATUM* 01-01-2011                                      \r\n:62F:C110103EUR000000005420,80\r\n:20:940xxxxxx4\r\n:25:3xxxxxx.013EUR                     \r\n:28:00000/00\r\n:60F:C110103EUR000000005420,80\r\n:61:110104C000000000015,00N1270309xxxxxx      Rxxxxxxxxxxxxxxxx                 \r\n:86:Contributie                                                      \r\n:61:110104C000000000015,00N1220531xxxxxx      Axxxxxxxxxxxxxx                   \r\n:86:CONTRIBUTIE                                                      \r\n:62F:C110104EUR000000005450,80\r\n"
  },
  {
    "path": "rhino-scripts/README.adoc",
    "content": "These are example JavaScript files that work with the jGnash API.\n\nUse these as a jump off point for writing your own."
  },
  {
    "path": "rhino-scripts/clear-budget-goal.js",
    "content": "// clears the budget goals for the selected account and budget\n\nload(\"nashorn:mozilla_compat.js\");  // Load compatibility script\n\nimportPackage(javax.swing);\nimportPackage(Packages.jgnash.ui);\nimportPackage(Packages.jgnash.ui.account);\nimportPackage(Packages.jgnash.ui.components);\nimportPackage(Packages.jgnash.ui.budget);\nimportPackage(Packages.jgnash.engine);\nimportPackage(Packages.jgnash.engine.budget);\n\nfunction debug(message) {   // helper function to print messages to the console\n    java.lang.System.out.println(message);\n}\n\n//show the console dialog to see the debug information\n// ConsoleDialog.show();   \n\n// this is how to get the default Engine instance\nvar engine = EngineFactory.getEngine(EngineFactory.DEFAULT);   \n\nvar account;\n\nvar dlg = new  AccountListDialog();\ndlg.setTitle(\"Select account to clear\");\ndlg.setVisible(true);\n\nif (dlg.getReturnStatus()) {\n\taccount = dlg.getAccount();\n    \n    debug(account.toString());\n    \n    var budgetCombo = new BudgetComboBox();\n    \n    dlg = new GenericCloseDialog(budgetCombo, \"Select Budget\");\n    dlg.setVisible(true);\n    \n    var budget = budgetCombo.getSelectedBudget();    \n    \n    debug(budget.toString());\n    \n    // have budget, have account, now get the existing budget goal    \n    var budgetGoal = new BudgetGoal();\n    \n    // create array of amount and clear to zero\n    //noinspection JSValidateTypes\n    var BigDecimalArray = Java.type(\"java.math.BigDecimal[]\");\n    var amounts = new BigDecimalArray(BudgetGoal.PERIODS);\n    \n    for (var i=0; i < amounts.length; i++)\n    \tamounts[i] = java.math.BigDecimal.ZERO;\n    \n    // set the new amounts and update the budget\n    budgetGoal.setGoals(amounts);     \n    engine.updateBudgetGoals(budget, account, budgetGoal);\n}\n\ndebug(\"finished\");"
  },
  {
    "path": "rhino-scripts/create-random-transaction.js",
    "content": "// creates slightly random transaction data\n\nload(\"nashorn:mozilla_compat.js\");  // Load compatibility script\n\nimportPackage(javax.swing);\nimportPackage(Packages.jgnash.ui);\nimportPackage(Packages.jgnash.ui.account);\nimportPackage(Packages.jgnash.ui.components);\nimportPackage(Packages.jgnash.ui.budget);\nimportPackage(Packages.jgnash.engine);\nimportPackage(Packages.jgnash.engine.budget);\nimportPackage(Packages.java.time);\n\nvar rand = new java.util.Random();\n\nfunction debug(message) { // helper function to print messages to the console\n\tjava.lang.System.out.println(message);\n}\n\nfunction random(min, max) {\n\treturn min + rand.nextFloat() * (max - min);\n}\n\n// show the console dialog to see the debug information\n// ConsoleDialog.show();\n\n// this is how to get the default Engine instance\nvar engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\nvar debitAccount;\nvar creditAccount;\n\nvar dlg = new AccountListDialog();\ndlg.setTitle(\"Select debit account for transaction\");\ndlg.setVisible(true);\n\nif (dlg.getReturnStatus()) {\n\tdebitAccount = dlg.getAccount();\n\n\tdebug(debitAccount.toString());\n\n\tdlg.setTitle(\"Select credit account for transaction\");\n\tdlg.setVisible(true);\n\n\tif (dlg.getReturnStatus()) {\n\t\tcreditAccount = dlg.getAccount();\n\n\t\tvar amount = new java.math.BigDecimal(random(10.12, 32.23));\n\n\t\tvar tran = TransactionFactory.generateDoubleEntryTransaction(\n\t\t\t\tcreditAccount, debitAccount, amount, LocalDate.now(), \"memo\", \"payee\", \"\");\n\n\t\tengine.addTransaction(tran);\n\t}\n}\n\ndebug(\"finished\");"
  },
  {
    "path": "rhino-scripts/load-budget-goal.js",
    "content": "// loads the budget goals for the selected account and budget\n\nload(\"nashorn:mozilla_compat.js\");  // Load compatibility script\n\nimportPackage(javax.swing);\nimportPackage(Packages.jgnash.ui);\nimportPackage(Packages.jgnash.ui.account);\nimportPackage(Packages.jgnash.ui.components);\nimportPackage(Packages.jgnash.ui.budget);\nimportPackage(Packages.jgnash.engine);\nimportPackage(Packages.jgnash.engine.budget);\n\nfunction debug(message) { // helper function to print messages to the console\n\tjava.lang.System.out.println(message);\n}\n\n// show the console dialog to see the debug information\n// ConsoleDialog.show();\n\n// this is how to get the default Engine instance\nvar engine = EngineFactory.getEngine(EngineFactory.DEFAULT);\n\nvar account;\n\nvar dlg = new AccountListDialog();\ndlg.setTitle(\"Select account to set goals\");\ndlg.setVisible(true);\n\nif (dlg.getReturnStatus()) {\n\taccount = dlg.getAccount();\n\n\tdebug(account.toString());\n\n\tvar budgetCombo = new BudgetComboBox();\n\n\tdlg = new GenericCloseDialog(budgetCombo, \"Select Budget\");\n\tdlg.setVisible(true);\n\n\tvar budget = budgetCombo.getSelectedBudget();\n\n\tdebug(budget.toString());\n\n\t// have budget, have account, now get the existing budget goal\n\tvar budgetGoal = new BudgetGoal();\n\tbudgetGoal.setBudgetPeriod(BudgetPeriod.DAILY);\n\n\t// create array of amounts and clear to zero\n    //noinspection JSValidateTypes\n    var BigDecimalArray = Java.type(\"java.math.BigDecimal[]\");\n    var amounts = new BigDecimalArray(BudgetGoal.PERIODS);\n\n\t// zero all amounts first\n\tfor ( var i = 0; i < amounts.length; i++)\n\t\tamounts[i] = java.math.BigDecimal.ZERO;\n\n\tfor ( var j = 6; j < amounts.length;) {\n\t\tamounts[j] = new java.math.BigDecimal(\"1230.0\");\n\t\tj = j + 14;\n\t}\n\n\t// set the new amounts and update the budget\n\tbudgetGoal.setGoals(amounts);\n\tengine.updateBudgetGoals(budget, account, budgetGoal);\n}\n\ndebug(\"finished\");\n"
  },
  {
    "path": "rust-launcher/Cargo.toml",
    "content": "[package]\nname = \"rust-launcher\"\nversion = \"0.1.0\"\nauthors = [\"Craig Cavanaugh <jgnash.devel@gmail.com>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[target.'cfg(windows)'.build-dependencies]\nwinres = \"0.1.11\"\n\n[target.'cfg(windows)'.dependencies]\nwinreg = \"0.6.2\"\n\n[dependencies]\njava-locator = \"0.1.2\"\nlazy_static = \"1.4.0\"\nmsgbox = \"0.4.0\"\n\n[profile.release]\nopt-level = 'z'\ndebug = false\nlto = true\ndebug-assertions = false\ncodegen-units = 1\npanic = 'abort'\n\n[[bin]]\nname = \"jGnash\"\npath = \"src/main.rs\""
  },
  {
    "path": "rust-launcher/README.adoc",
    "content": "Build:\n\ncargo build --release\n"
  },
  {
    "path": "rust-launcher/build.rs",
    "content": "// adds an icon to the windows executable\n\n#[cfg(target_family = \"windows\")]\nextern crate winres;\n\n#[cfg(target_family = \"windows\")]\nfn main() {\n    if cfg!(target_os = \"windows\") {\n        let mut res = winres::WindowsResource::new();\n        res.set_icon(\"gnome-money.ico\");\n        res.compile().unwrap();\n    }\n}\n\n#[cfg(target_family = \"unix\")]\nfn main() {\n    // empty for unix build\n}\n"
  },
  {
    "path": "rust-launcher/src/main.rs",
    "content": "#![windows_subsystem = \"windows\"]\n/*\n * jGnash, a personal finance application\n * Copyright (C) 2001-2020 Craig Cavanaugh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\nextern crate java_locator;\nextern crate lazy_static;\nextern crate msgbox;\n\n#[cfg(target_family = \"windows\")]\nextern crate winreg;\n\nuse std::env;\nuse std::f32;\nuse std::ops::Add;\nuse std::path::PathBuf;\nuse std::process;\nuse std::process::{Command, ExitStatus};\n\nuse lazy_static::lazy_static;\nuse msgbox::IconType;\n\n#[cfg(target_family = \"windows\")]\nuse winreg::enums::*;\n\n#[cfg(target_family = \"windows\")]\nuse winreg::RegKey;\n\n/// The Minimum version of Java required\nconst MIN_JAVA_VERSION: f32 = 11.0 - (2.0 * f32::EPSILON);\n\n/// return value when jGnash exits with a reboot request\nconst REBOOT_EXIT: i32 = 100;\n\n// lib path\n#[cfg(target_family = \"windows\")]\nstatic LIB: &str = \"\\\\lib\\\\\";\n#[cfg(target_family = \"unix\")]\nstatic LIB: &str = \"/lib/\";\n\n// locate_java_home() is time consuming; Execute only once and save as a static\nlazy_static! {\n    static ref JAVA_HOME: Option<String> = { get_java_home() };\n}\n\n#[cfg(target_family = \"unix\")]\nlazy_static! {\n    static ref JAVA_EXE: String = { JAVA_HOME.as_ref().unwrap().clone().add(\"/bin/java\") };\n}\n\n#[cfg(target_family = \"windows\")]\nlazy_static! {\n    static ref JAVA_EXE: String = { JAVA_HOME.as_ref().unwrap().clone().add(\"\\\\bin\\\\javaw.exe\") };\n}\n\nfn main() {\n    let v = get_java_version();\n\n    if v >= MIN_JAVA_VERSION {\n        match JAVA_HOME.as_ref() {\n            Some(_) => {\n                let exit_status = launch_jgnash().code().unwrap();\n\n                if exit_status == REBOOT_EXIT {\n                    // relaunch jGnash after fx libs have downloaded\n                    process::exit(launch_jgnash().code().unwrap());\n                }\n                process::exit(exit_status);\n            }\n            None => msgbox::create(\n                \"Error\",\n                \"Unable to locate a valid Java installation.\\n\\n\n                 Please check your configuration or download a JVM from https://adoptopenjdk.net.\",\n                IconType::Error,\n            ),\n        }\n    } else {\n        msgbox::create(\n            \"Error\",\n            \"Your Java installation is too old or misconfigured.\\n\\n\\\n             Please download a JVM from https://adoptopenjdk.net.\",\n            IconType::Error,\n        )\n    }\n}\n\nfn launch_jgnash() -> ExitStatus {\n    // collect environment variables; the fist is the path that launched the program\n    let mut args: Vec<String> = env::args().collect();\n    args.remove(0);\n\n    let class_path = get_execution_path()\n        .as_os_str()\n        .to_str()\n        .unwrap()\n        .to_string()\n        .add(LIB)\n        .add(\"*\");\n\n    Command::new(&*JAVA_EXE)\n        .arg(\"-classpath\")\n        .arg(&class_path)\n        .arg(\"jgnash.app.jGnash\")\n        .arg(args.join(\" \"))\n        .status()\n        .expect(\"command failed to start\")\n}\n\nfn get_execution_path() -> PathBuf {\n    match env::current_exe() {\n        Ok(mut path) => {\n            path.pop(); // pop off the name of the executable\n            path\n        }\n        Err(_e) => PathBuf::new(),\n    }\n}\n\n/// Executes java --version and extracts the version\nfn get_java_version() -> f32 {\n    let mut version = -1.0_f32;\n\n    let output = Command::new(&*JAVA_EXE)\n        .arg(\"--version\")\n        .output()\n        .expect(\"failed to execute command\");\n\n    let st = String::from_utf8(output.stdout).unwrap();\n\n    if !st.is_empty() {\n        // look for the first available number\n        let mut decimal_found = false;\n        let mut version_string = String::new();\n\n        // munch through the string one char at a time and extract the first decimal\n        for c in st.chars() {\n            if c.is_ascii_digit() {\n                version_string.push(c);\n            }\n\n            if c == '.' {\n                if decimal_found {\n                    // we don't want the tertiary decimal\n                    break;\n                }\n\n                version_string.push(c);\n                decimal_found = true;\n            }\n\n            // must have found a xx.xx decimal instead of xx.xx.x\n            if c.is_ascii_whitespace() && decimal_found {\n                break;\n            }\n        }\n\n        if !version_string.is_empty() {\n            version = match version_string.parse::<f32>() {\n                Ok(f) => f,\n                Err(_e) => {\n                    eprintln!(\"failed to parse: {}\", version_string);\n                    version\n                }\n            }\n        }\n    }\n\n    version\n}\n\nfn get_java_home() -> Option<String> {\n    match &env::var(\"JAVA_HOME\") {\n        Ok(s) => {\n            if s.is_empty() {\n                // do more\n            } else {\n                return Some(s.clone());\n            }\n        }\n        Err(_) => {} // do nothing\n    }\n\n    // if an env variable is not found, check registry if compiled for windows\n    #[cfg(target_family = \"windows\")]\n    match &RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey(\"SOFTWARE\\\\JavaSoft\\\\JDK\") {\n        Ok(m_reg_key) => {\n            let version_string: String = m_reg_key.get_value(\"CurrentVersion\").unwrap();\n\n            let version = match version_string.parse::<f32>() {\n                Ok(f) => f,\n                Err(_e) => {\n                    eprintln!(\n                        \"failed to parse version string for the registry: {}\",\n                        version_string\n                    );\n                    return Some(String::new());\n                }\n            };\n\n            if version >= MIN_JAVA_VERSION {\n                let meta_data = m_reg_key.query_info().unwrap();\n\n                // if there are sub keys, then we have found a java installation\n                if meta_data.sub_keys > 0 {\n                    for k in m_reg_key.enum_keys() {\n                        match m_reg_key.open_subkey(&k.unwrap()) {\n                            Ok(sub_key) => {\n                                let s_val = sub_key.get_value(\"JavaHome\");\n\n                                if s_val.is_ok() {\n                                    let mut jvm_path: String = s_val.unwrap();\n                                    jvm_path.pop(); // strip the last path separator\n\n                                    return Some(jvm_path); // return the path\n                                }\n                            }\n                            Err(_) => {} // do nothing, pass through\n                        }\n                    }\n                }\n            }\n        }\n        Err(_) => {} // do nothing, pass through\n    }\n\n    // pass through to java_locator, it will search known directories\n    Some(java_locator::locate_java_home().unwrap())\n}\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "// extract plugin versions from gradle.properties\nval testFxVersion: String by settings\nval monocleVersion: String by settings\n\npluginManagement {\n    val javafxPluginVersion: String by settings\n    val versionsPluginVersion: String by settings\n    val macAppBundleVersion: String by settings\n\n    plugins {\n        id(\"org.openjfx.javafxplugin\") version javafxPluginVersion\n        id (\"com.github.ben-manes.versions\") version versionsPluginVersion\n        id(\"edu.sc.seis.macAppBundle\") version macAppBundleVersion\n    }\n}\n\nenableFeaturePreview(\"VERSION_ORDERING_V2\")\n\nrootProject.name = \"jgnash\"\n\ninclude (\"jgnash-bayes\", \"jgnash-resources\", \"jgnash-core\", \"jgnash-convert\",\n        \"jgnash-plugin\", \"jgnash-fx\", \"jgnash-report-core\", \"jgnash-fx-test-plugin\", \"mt940\", \"jgnash-tests\")\n"
  },
  {
    "path": "spelling.dic",
    "content": "Modena\ncavanaugh\npathname\nAMORTIZEOBJECT\nCREDITLIMIT\nVATOBJECT\njgnash\nbackends\nUSEDPASSWORD\nyyyy\nMMMMM\nYYYYMMDDHHMMSS\nyyyyMMddHHmmss\nfilesep\nUSASCII\nCSUNICODE\nOFXHEADER\nisdb\nstmtrs\nSubtree\nISIN\nxlsx\nautosize\ntestworkbook\nunregister\nEXCHANGERATE\nnano\nprintln\nemptyfile\nFILEEXTREGEX\ntempfile\ntestdata\nabsolutepath\nrepost\ncodec\nCodec\nrawtypes\nMulti\nhttpuser\nhttppass\nuseproxy\nuseauth\nproxyhost\nproxyport\nNODEHTTP\nHTTPUSER\nHTTPPASS\nUSEPROXY\nUSEAUTH\nPROXYHOST\nPROXYPORT\nINCOMEEXPENSE\nBOTHSIDES\ncraig\nMaven\nCitibank\ntooltip\nJavahelp\njhindexer\njavahelp\ncodehaus\nxalan\nxerces\nsourcepath\nxstream\njxlayer\njgoodies\nlgpl\nsharealike\nCrosstab\nPotochkin\nsubcomponents\nlcgap\nlgap\nViewport\nSCROLLPANE\nugap\nrgap\nnlgap\nsignum\nconcat\nbxds\nBxds\nMonospace\nAntialias\nantialiasing\nfitid\nFitidunicode\nwildcard\nyahoo\nnavneet\nkarnani\ngnome\ngnome\nccavanaugh\ninsubstantial\nlocales\nsubdirectories\nhyperlink\nplugins\narnout\nengelen\nmiroslav\nholubec\nbayes\ndutch\nplugin\npayee\npranay\nkumar\npayees\nvida\nproxy\ntimestamp\ndebit\nportfolio\ntrufanov\naleksey\nsegfault\nmarcelo\nabeldaño\nportuguese\nsparklines\npietro\ndavide\nklemen\nzagar\nreimplement\npreload\nprickett\njeff\nveilleux\ndany\nchad\nmina\nchris\nnimbus\nembeddable\nannuities\nvincent\nfrison\nedelson\nunreconciled\nsortable\nACCTID\nACCTTYPE\nAVAILBAL\nBALAMT\nBANKACCTFROM\nCCACCTFROM\nINVACCTFROM\nBANKID\nREFNUM\nPAYEEID\nORIGCURRENCY\nNEWFILEUID\nOLDFILEUID\nOFXSGML\nLEDGERBAL\nCCSTMTRS\nINVSTMTRS\nSIMPLEINVEST\nBRANCHID\nBANKTRANLIST\nINVTRANLIST\nBROKERID\nCHECKNUM\nCURDEF\nCREDITLINE\nMONEYMRKT\nDTASOF\nDTEND\nDTPOSTED\nDTUSER\nDTSERVER\nDTSTART\nSIGNONMSGSRSV1\nSONRS\nSTMTTRN\nSTMTTRNRS\nCCSTMTTRNRS\nINVSTMTTRNRS\nTRNAMT\nTRNTYPE\nTRNUID\nUSERKEY\nBANKMSGSRSV1\nCREDITCARDMSGSRSV1\nINVSTMTMSGSRSV1\nSECLISTMSGSRSV1\nASSETCLASS\nSECLIST\nINTUBID\nINTUUSERID\nCUSIP\nUNIQUEID\nUNIQUEIDTYPE\nUNITPRICE\nSUBACCTFUND\nSUBACCTSEC\nSIGNONMSGSRSV1\nSECID\nDTSETTLE\nDTTRADE\nINVBUY\nINVTRAN\nBUYSTOCK\nBUYTYPE\nINVBANKTRAN\nSELLSTOCK\nSELLTYPE\nCCACCTTO\nBANKACCTTO\nrelativize\nAPI's\nxmlns\nsodipodi\nopengl\njavase\ndocbook\nhelpset\njhelpmap\njhelptoc\ndocbkx\nxlink\nrootid\nrefentry\nindexterm\nguimenu\nnoncommetcially\nAffero\nsaveas\nReentrant\nreentrant\ntmpdir\njdbc\nJDBC\nNullable\nDOUBLEENTRY\nSPLITENTRY\nSINGLENTRY\nADDSHARE\nBUYSHARE\nDIVIDEND\nREINVESTDIV\nREMOVESHARE\nRETURNOFCAPITAL\nSELLSHARE\nSPLITSHARE\nMERGESHARE\nCURSYM\nCURRATE\nDTEXPIRE\nSHPERCTRCT\nFIID\nOPTINFO\nOPTTYPE\nSTRIKEPRICE\nINCOMETYPE\nLOANID\nLOANPRINCIPAL\nLOANINTEREST\nINV401KSOURCE\nDTPAYROLL\nPRIORYEARCONTRIB\nINV401KBAL\nTAXEXEMPT\ndelegator\nDelegator\nsignon\nDTPROFUP\nDTACCTUP\nDIRECTDEP\nuifx\nwinnt\nopenwin\nTRANSACTIONENTRY\ndarkred\nhgap\nvgap\nlightgray\nMSFT\ndarkgoldenrod\nthoughtworks\nFILEFORMAT\nfileformat\njasperreports\nlicensor\nfacto\naccessors\nhibernate\nparsable\nJIDE\nversioning\nfxml\nhtml\npopup\nunicode\nglyph\ndörr\nerik\nleif\nbayesian\nfx\ndatastore\nvirtualized\nRunnables\nGradle\nversioning\nuninstallation\nINVACCTTO\nnop\npdfbox\nbootloader\nwinres\njavaw\nmsgbox\nmisconfigured\nwinreg\nRunnables\nrespawn\nChrono\nPicocli\nHikari\n"
  },
  {
    "path": "windows 10 Java reg fix.reg",
    "content": "Windows Registry Editor Version 5.00\n[HKEY_LOCAL_MACHINE\\Software\\JavaSoft\\Prefs]\n"
  }
]